先前我們所看的例子太基礎(chǔ)也沒有實(shí)際用途。讓我們看計(jì)數(shù)器的例子,寫一個(gè)DUT的參考模型以及用C寫的Checker程序,并把它鏈入到Verilog的Testbench。首先列出我們要用PLI寫C模型的要求。
VerilogPLI提供一系列滿足以上要求的程序(函數(shù))。
PLI 應(yīng)用的SPEC
我們定義使用PLI的計(jì)數(shù)器testbench的要求。該P(yáng)LI函數(shù)我們稱作$counter_monitor
調(diào)用C函數(shù)
用C寫計(jì)數(shù)器非常簡單,但是什么時(shí)候我們要增加計(jì)數(shù)器的值呢?所以我們就需要監(jiān)測時(shí)鐘信號的變化(順便說一句,通過verilog代碼驅(qū)動(dòng)復(fù)位和時(shí)鐘信號是個(gè)好習(xí)慣)一旦時(shí)鐘發(fā)生變化,計(jì)數(shù)器函數(shù)就要被執(zhí)行。這可以通過以下的函數(shù)實(shí)現(xiàn):
使用acc_vcl_add程序。該函數(shù)的語法可以查閱Verilog PLI LRM基本上acc_vcl_add程序能檢測許多信號,一旦任何一個(gè)信號改變就會(huì)調(diào)用用戶定義的函數(shù)(也就是定制的C程序)。VCL程序有4個(gè)自變量:
vcl_verilog_strength -- 強(qiáng)度監(jiān)測
acc_vcl_add(net, display_net, netname, vcl_verilog_logic);
C代碼 - 基礎(chǔ)
Counter_monitor是我們在verilog testbench中將要調(diào)用的C函數(shù)。就像任何其他的C代碼,我們需要包含針對我們所要開發(fā)的特定的應(yīng)用的頭文件。我們這里,需要包含acc 程序include文件。
access函數(shù) acc_initialize 初始化access程序的環(huán)境,在程序調(diào)用任何的access程序前必須要調(diào)用的。在退出調(diào)用access程序的C應(yīng)用程序前,在程序的最后必須要退出access程序環(huán)境,調(diào)用acc_close。
1 | #include "acc_user.h" | |
2 |
| |
3 | handle clk ; | |
4 | handle reset ; | |
5 | handle enable ; | |
6 | handle dut_count ; | |
7 | void counter (); | |
8 |
| |
9 | void counter_monitor() { | |
10 |
| acc_initialize(); |
11 |
| clk = acc_handle_tfarg(1); |
12 |
| reset = acc_handle_tfarg(2); |
13 |
| enable = acc_handle_tfarg(3); |
14 |
| dut_count = acc_handle_tfarg(4); |
15 |
| acc_vcl_add(clk,counter,null,vcl_verilog_logic); |
16 |
| acc_close(); |
17 | } | |
18 |
| |
19 | void counter () { | |
20 |
| io_printf( "Clock changed staten" ); |
21 | } |
為了能accessing verilog對象,我們使用句柄。句柄是指預(yù)定義的數(shù)據(jù)類型,它是是設(shè)計(jì)層次中某個(gè)對象的指針。每個(gè)句柄傳遞給access函數(shù)有關(guān)唯一可以accessible對象的類型信息,以及如何哪里能找到有關(guān)此對象的信息。但是,我們?nèi)绾伟涯硞€(gè)特定對象的信息給句柄呢?我們可以通過許多途徑,但是現(xiàn)在我們用verilog采用參數(shù)傳遞的方式給$counter_monitor, 這些參數(shù)在C程序中可以采用acc_handle_tfarg()函數(shù)獲得,這里自變量和代碼中一樣是數(shù)字。
因此clk = acc_handle_tfarg(1) 使得clk作為第一參數(shù)傳遞的句柄。類似我們給所有的句柄賦值?,F(xiàn)在我們可以把clk加入我們需要檢測的信號列表中,acc_vcl_add(clk,counter,null,vcl_verilog_logic)。這里clk是句柄,counter是當(dāng)clk發(fā)生變化時(shí),需要執(zhí)行的用戶函數(shù)。
函數(shù)counter()就不作任何解釋了,它是類似于hello world簡單的代碼。
Verilog 代碼
下面是計(jì)數(shù)器例子的簡單的testbench。We call the C-function using the syntax shown in co
1 | module counter_tb(); | |
2 | reg enable; | |
3 | reg reset; | |
4 | reg clk_reg; | |
5 | wire clk; | |
6 | wire [3:0] count; | |
7 |
| |
8 | initial begin | |
9 |
| enable = 0; |
10 |
| clk_reg = 0; |
11 |
| reset = 0; |
12 |
| $display( "%g , Asserting reset" , $time); |
13 |
| #10 reset = 1; |
14 |
| #10 reset = 0; |
15 |
| $display ( "%g, Asserting Enable" , $time); |
16 |
| #10 enable = 1; |
17 |
| #55 enable = 0; |
18 |
| $display ( "%g, Deasserting Enable" , $time); |
19 |
| #1 $display ( "%g, Terminating Simulator" , $time); |
20 |
| #1 $finish; |
21 | end | |
22 |
| |
23 | always begin | |
24 |
| #5 clk_reg = !clk_reg; |
25 | end | |
26 |
| |
27 | assign clk = clk_reg; | |
28 |
| |
29 | initial begin | |
30 |
| $counter_monitor(counter_tb.clk, counter_tb.reset, counter_tb.enable, counter_tb.count); |
31 | end | |
32 |
| |
33 | counter U( | |
34 | .clk (clk), | |
35 | .reset (reset), | |
36 | .enable (enable), | |
37 | .count (count) | |
38 | ); | |
39 |
| |
40 | endmodule |
使用不同的仿真器,編譯和運(yùn)行程序不同。當(dāng)你運(yùn)行以上的代碼,我們將得到以下的輸出:
0 , Asserting reset Clock changed state Clock changed state Clock changed state 20, Asserting Enable Clock changed state Clock changed state Clock changed state Clock changed state Clock changed state Clock changed state Clock changed state Clock changed state Clock changed state Clock changed state Clock changed state Clock changed state Clock changed state 85, Deasserting Enable Clock changed state 86, Terminating Simulator |
|
|
|
C代碼 -- 完整
現(xiàn)在我們可以看見當(dāng)時(shí)鐘發(fā)生變化時(shí),我們的函數(shù)就會(huì)被調(diào)用。我們可以寫我們的計(jì)數(shù)器代碼了。但是,有一個(gè)問題,每次計(jì)數(shù)器調(diào)用的時(shí)候,就會(huì)退出也就會(huì)丟失內(nèi)部變量的值。我們有2重方法來保持變量的值。
由于我們只有一個(gè)變量,我們可以采用第一種解決方法,也就是申明count為全局變量為了編寫與計(jì)數(shù)器等價(jià)的模型,輸入到DUT和代碼checker的clock,reset, enable信號以及DUT count的輸出都需要。為了從verilog獲取值,我們使用PLI函數(shù):
acc_fetch_value(handle,"format")
但是該值返回的是字符串,因此我們需要把它轉(zhuǎn)變?yōu)檎麛?shù)。pli_conv是完成這個(gè)轉(zhuǎn)換的函數(shù)。當(dāng)DUT和TB計(jì)數(shù)值不相同時(shí),函數(shù)tf_dofinish()用做終止該仿真。
1 | #include "acc_user.h" | |||
2 |
| |||
3 | typedef char * string; | |||
4 | handle clk ; | |||
5 | handle reset ; | |||
6 | handle enable ; | |||
7 | handle dut_count ; | |||
8 | int count ; | |||
9 | int sim_time; | |||
10 | string high = "1" ; | |||
11 | void counter (); | |||
12 | int pli_conv (string in_string, int no_bits); | |||
13 |
| |||
14 | void counter_monitor() { | |||
15 |
| acc_initialize(); | ||
16 |
| clk = acc_handle_tfarg(1); | ||
17 |
| reset = acc_handle_tfarg(2); | ||
18 |
| enable = acc_handle_tfarg(3); | ||
19 |
| dut_count = acc_handle_tfarg(4); | ||
20 |
| acc_vcl_add(clk,counter,null,vcl_verilog_logic); | ||
21 |
| acc_close(); | ||
22 | } | |||
23 |
| |||
24 | void counter () { | |||
25 |
| p_acc_value value; | ||
26 |
| sim_time = tf_gettime(); | ||
27 |
| string i_reset = acc_fetch_value(reset, "%b" ,value); | ||
28 |
| string i_enable = acc_fetch_value(enable, "%b" ,value); | ||
29 |
| string i_count = acc_fetch_value(dut_count, "%b" ,value); | ||
30 |
| string i_clk = acc_fetch_value(clk, "%b" ,value); | ||
31 |
| int size_in_bits= acc_fetch_size (dut_count); | ||
32 |
| int tb_count = 0; | ||
33 |
| // Counter function goes here | ||
34 |
| if (*i_reset == *high) { | ||
35 |
|
| count = 0; | |
36 |
|
| io_printf( "%d, dut_info : Counter is resetn" , sim_time); | |
37 |
| } | ||
38 |
| else if ((*i_enable == *high) && (*i_clk == *high)) { | ||
39 |
|
| if ( count == 15 ) { | |
40 |
|
|
| count = 0; |
41 |
|
| } else { | |
42 |
|
|
| count = count + 1; |
43 |
|
| } | |
44 |
| } | ||
45 |
| // Counter Checker function goes checker logic goes here | ||
46 |
| if ((*i_clk != *high) && (*i_reset != *high)) { | ||
47 |
|
| tb_count = pli_conv(i_count,size_in_bits); | |
48 |
|
| if (tb_count != count) { | |
49 |
|
|
| io_printf( "%d, dut_error : Expect value %d, Got value %dn" , sim_time, count, tb_count); |
50 |
|
|
| tf_dofinish(); |
51 |
|
| } else { | |
52 |
|
|
| io_printf( "%d, dut_info : Expect value %d, Got value %dn" , sim_time, count, tb_count); |
53 |
|
| } | |
54 |
| } | ||
55 | } | |||
56 |
| |||
57 | // Multi-bit vector to integer conversion. | |||
58 | int pli_conv (string in_string, int no_bits) { | |||
59 |
| int conv = 0; | ||
60 |
| int i = 0; | ||
61 |
| int j = 0; | ||
62 |
| int bin = 0; | ||
63 |
| for ( i = no_bits-1; i >= 0; i = i - 1) { | ||
64 |
|
| if (*(in_string + i) == 49) { | |
65 |
|
|
| bin = 1; |
66 |
|
| } else if (*(in_string + i) == 120) { | |
67 |
|
|
| io_printf ( "%d, Warning : X detectedn" , sim_time); |
68 |
|
|
| bin = 0; |
69 |
|
| } else if (*(in_string + i) == 122) { | |
70 |
|
|
| io_printf ( "%d, Warning : Z detectedn" , sim_time); |
71 |
|
|
| bin = 0; |
72 |
|
| } else { | |
73 |
|
|
| bin = 0; |
74 |
|
| } | |
75 |
|
| conv = conv + (1 << j)*bin; | |
76 |
|
| j ++; | |
77 |
| } | ||
78 |
| return conv; | ||
79 | } |
與仿真器鏈接
先前我們見到的計(jì)數(shù)器例子,我們將用下面的仿真器進(jìn)行鏈接。
在此教程中,我們使用linux。如果你想知道如何在windows或者solaris下鏈接,請參考仿真器的手冊查看詳細(xì)資料。
VCS
使用VCS仿真器,我們需要?jiǎng)?chuàng)建一個(gè)tab文件。對于我們的例子,tab文件如下所示:
$counter_monitor call=counter_monitor acc=rw:*
這里 $counter_monitor是在verilog代碼中的用戶自定義函數(shù)。call=counter_monitor是當(dāng)verilog調(diào)用$counter_monitor時(shí)調(diào)用的C函數(shù)。acc=rw:*是告訴我們要使用access函數(shù)去讀寫仿真器內(nèi)部數(shù)據(jù)。:*意味著應(yīng)用于設(shè)計(jì)中的所有模塊。
編譯代碼的命令行選項(xiàng)如下:
vcs -R -P pli_counter.tab pli_counter_tb.v counter.v pli_full_example.c -CFLAGS "-g -I$VCS_HOME/`vcs -platform`/lib" +acc+3
由于我們要使用返回值,我們使用+acc+3,剩下的選項(xiàng)簡單,你可以查閱VCS的用戶指南。
Modelsim
類似于VCS,modelsim仿真器是用PLI有它自己的方式。我們需要?jiǎng)?chuàng)建函數(shù)列出所有在verilog中要使用的用戶自定義函數(shù)以及對應(yīng)的C函數(shù)。與VCS不同,我們需要在C文件中寫明,如下:
1 | #include "veriuser.h" | |
2 | #include "pli_full_example.c" | |
3 |
| |
4 | s_tfcell veriusertfs[] = { | |
5 |
| {usertask, 0, 0, 0, counter_monitor, 0, "$counter_monitor" }, |
6 |
| {0} // last entry must be 0 |
7 |
| }; |
vlib work
vlog pli_counter_tb.v counter.v
gcc -c -g -I$MODEL/include pli_full_example_modelsim.c
ld -shared -E -o pli_full_example.sl pli_full_example_modelsim.o
vsim -c counter_tb -pli pli_full_example.sl
在vsim命令行中,輸入run -all開始仿真。
查閱modelsim的用戶指南了解詳細(xì)信息以及了解如何編譯和鏈接在windows下。
計(jì)數(shù)器仿真結(jié)果
0 , Asserting reset 10, dut_info : Counter is reset 15, dut_info : Counter is reset 20, Asserting Enable 20, dut_info : Expect value 0, Got value 0 30, dut_info : Expect value 0, Got value 0 40, dut_info : Expect value 1, Got value 1 50, dut_info : Expect value 2, Got value 2 60, dut_info : Expect value 3, Got value 3 70, dut_info : Expect value 4, Got value 4 80, dut_info : Expect value 5, Got value 5 85, Deasserting Enable 86, Terminating Simulator |
先前我們所看的例子太基礎(chǔ)也沒有實(shí)際用途。讓我們看計(jì)數(shù)器的例子,寫一個(gè)DUT的參考模型以及用C寫的Checker程序,并把它鏈入到Verilog的Testbench。首先列出我們要用PLI寫C模型的要求。
VerilogPLI提供一系列滿足以上要求的程序(函數(shù))。
PLI 應(yīng)用的SPEC
我們定義使用PLI的計(jì)數(shù)器testbench的要求。該P(yáng)LI函數(shù)我們稱作$counter_monitor
調(diào)用C函數(shù)
用C寫計(jì)數(shù)器非常簡單,但是什么時(shí)候我們要增加計(jì)數(shù)器的值呢?所以我們就需要監(jiān)測時(shí)鐘信號的變化(順便說一句,通過verilog代碼驅(qū)動(dòng)復(fù)位和時(shí)鐘信號是個(gè)好習(xí)慣)一旦時(shí)鐘發(fā)生變化,計(jì)數(shù)器函數(shù)就要被執(zhí)行。這可以通過以下的函數(shù)實(shí)現(xiàn):
使用acc_vcl_add程序。該函數(shù)的語法可以查閱Verilog PLI LRM基本上acc_vcl_add程序能檢測許多信號,一旦任何一個(gè)信號改變就會(huì)調(diào)用用戶定義的函數(shù)(也就是定制的C程序)。VCL程序有4個(gè)自變量:
vcl_verilog_strength -- 強(qiáng)度監(jiān)測
acc_vcl_add(net, display_net, netname, vcl_verilog_logic);
C代碼 - 基礎(chǔ)
Counter_monitor是我們在verilog testbench中將要調(diào)用的C函數(shù)。就像任何其他的C代碼,我們需要包含針對我們所要開發(fā)的特定的應(yīng)用的頭文件。我們這里,需要包含acc 程序include文件。
access函數(shù) acc_initialize 初始化access程序的環(huán)境,在程序調(diào)用任何的access程序前必須要調(diào)用的。在退出調(diào)用access程序的C應(yīng)用程序前,在程序的最后必須要退出access程序環(huán)境,調(diào)用acc_close。
1 | #include "acc_user.h" | |
2 |
| |
3 | handle clk ; | |
4 | handle reset ; | |
5 | handle enable ; | |
6 | handle dut_count ; | |
7 | void counter (); | |
8 |
| |
9 | void counter_monitor() { | |
10 |
| acc_initialize(); |
11 |
| clk = acc_handle_tfarg(1); |
12 |
| reset = acc_handle_tfarg(2); |
13 |
| enable = acc_handle_tfarg(3); |
14 |
| dut_count = acc_handle_tfarg(4); |
15 |
| acc_vcl_add(clk,counter,null,vcl_verilog_logic); |
16 |
| acc_close(); |
17 | } | |
18 |
| |
19 | void counter () { | |
20 |
| io_printf( "Clock changed staten" ); |
21 | } |
為了能accessing verilog對象,我們使用句柄。句柄是指預(yù)定義的數(shù)據(jù)類型,它是是設(shè)計(jì)層次中某個(gè)對象的指針。每個(gè)句柄傳遞給access函數(shù)有關(guān)唯一可以accessible對象的類型信息,以及如何哪里能找到有關(guān)此對象的信息。但是,我們?nèi)绾伟涯硞€(gè)特定對象的信息給句柄呢?我們可以通過許多途徑,但是現(xiàn)在我們用verilog采用參數(shù)傳遞的方式給$counter_monitor, 這些參數(shù)在C程序中可以采用acc_handle_tfarg()函數(shù)獲得,這里自變量和代碼中一樣是數(shù)字。
因此clk = acc_handle_tfarg(1) 使得clk作為第一參數(shù)傳遞的句柄。類似我們給所有的句柄賦值?,F(xiàn)在我們可以把clk加入我們需要檢測的信號列表中,acc_vcl_add(clk,counter,null,vcl_verilog_logic)。這里clk是句柄,counter是當(dāng)clk發(fā)生變化時(shí),需要執(zhí)行的用戶函數(shù)。
函數(shù)counter()就不作任何解釋了,它是類似于hello world簡單的代碼。