(筆記) 如何將參數從Verilog傳到C? (SOC) (Verilog) (Verilog PLI)
自行寫一個System Task,能夠接受由Verilog的reg或wire傳給C的值。
Introduction
使用環境 : Cadense NC-Verilog 5.4 + Visual C++ 6.0
在此文件,將學習到
1.如何將參數從Verilog傳到C?
2.如何撰寫簡單的compiletf?
3.如何從C抓到Verilog傳來的參數?
show_value.c / C
2 #include "vpi_user.h"
3
4 PLI_INT32 show_value_compiletf(PLI_BYTE8 *user_data) {
5 vpiHandle systf_handle, arg_iterator, arg_handle;
6 PLI_INT32 arg_type;
7
8 // obtain a handle to the system task instance
9 systf_handle = vpi_handle(vpiSysTfCall, NULL);
10 if (systf_handle == NULL) {
11 vpi_printf("ERROR: $show_value failed to obtain systf handle\n");
12 vpi_control(vpiFinish, 0);
13
14 return -1;
15 }
16
17 // obtain handles to system task arguments
18 arg_iterator = vpi_iterate(vpiArgument, systf_handle);
19 if (arg_iterator == NULL) {
20 vpi_printf("ERROR: $show_value requires 1 argument\n");
21 vpi_control(vpiFinish, 0);
22
23 return -1;
24 }
25
26 // check the type of object in system task arguments
27 arg_handle = vpi_scan(arg_iterator);
28 arg_type = vpi_get(vpiType, arg_handle);
29 if (arg_type != vpiNet && arg_type != vpiReg) {
30 vpi_printf("ERROR: $show_value arg must be a net or a reg\n");
31 vpi_free_object(arg_iterator);
32 vpi_control(vpiFinish, 0);
33
34 return -1;
35 }
36
37 // check only 1 system task argument
38 arg_handle = vpi_scan(arg_iterator);
39 if (arg_handle != NULL) {
40 vpi_printf("ERROR: $show_value can only have 1 argument\n");
41 vpi_free_object(arg_iterator);
42 vpi_control(vpiFinish, 0);
43
44 return -1;
45 }
46
47 return 0;
48 }
49
50 PLI_INT32 show_value_calltf(PLI_BYTE8 *user_data) {
51 vpiHandle systf_handle, arg_iterator, arg_handle, net_handle;
52 s_vpi_value current_value;
53
54 // obtain a handle to the system task instance
55 systf_handle = vpi_handle(vpiSysTfCall, NULL);
56
57 // obtain hadle to system task argument
58 // compiletf has already verified only 1 arg with corret type
59 arg_iterator = vpi_iterate(vpiArgument, systf_handle);
60 net_handle = vpi_scan(arg_iterator);
61 vpi_free_object(arg_iterator);
62
63 // read current value
64 current_value.format = vpiHexStrVal; // read a value as a string
65 vpi_get_value(net_handle, ¤t_value);
66 vpi_printf("Signal %s ", vpi_get_str(vpiFullName, net_handle));
67 vpi_printf("has the value %s\n", current_value.value.str);
68
69 return 0;
70 }
71
72 void show_value_register() {
73 s_vpi_systf_data tf_data;
74
75 tf_data.type = vpiSysTask;
76 tf_data.tfname = "$show_value";
77 tf_data.calltf = show_value_calltf;
78 tf_data.compiletf = show_value_compiletf;
79
80 vpi_register_systf(&tf_data);
81 }
Step 1:
撰寫compiletf,判斷由Verilog傳給C的參數是否合法
第5行
vpiHandle是VPI自訂的型態,為pointer to Verilog object。systf_handle為pointer to system task,arg_iterator為pointer to argument iterator,arg_handle為pointer to argument。
第8行
systf_handle = vpi_handle(vpiSysTfCall, NULL);
if (systf_handle == NULL) {
vpi_printf("ERROR: $show_value failed to obtain systf handle\n");
vpi_control(vpiFinish, 0);
return -1;
}
由vpi_handle()取得system task的handle,若為NULL,則顯示錯誤訊息並且結束模擬。
17行
arg_iterator = vpi_iterate(vpiArgument, systf_handle);
if (arg_iterator == NULL) {
vpi_printf("ERROR: $show_value requires 1 argument\n");
vpi_control(vpiFinish, 0);
return -1;
}
由vpi_itrator()獲得argument iterator,為參數的集合,若為NULL,則顯示錯誤訊息並且結束模擬。
26行
arg_handle = vpi_scan(arg_iterator);
arg_type = vpi_get(vpiType, arg_handle);
if (arg_type != vpiNet && arg_type != vpiReg) {
vpi_printf("ERROR: $show_value arg must be a net or a reg\n");
vpi_free_object(arg_iterator);
vpi_control(vpiFinish, 0);
return -1;
}
檢查參數是否為reg或者wire型態,由vpi_scan()從argument iterator萃取出argument handle,再由vpi_get()獲得參數的型別做判斷,若不是wire或reg,則顯示錯誤訊息並結束模擬。
37行
arg_handle = vpi_scan(arg_iterator);
if (arg_handle != NULL) {
vpi_printf("ERROR: $show_value can only have 1 argument\n");
vpi_free_object(arg_iterator);
vpi_control(vpiFinish);
return -1;
}
由於$show_value()只允許Verilog傳入一個參數,在此判斷user是否只傳入一個參數,若由vpi_scan()抓取argument iterator還能抓到值,表示不只一個參數,則顯示錯誤訊息並結束模擬。
Step 2:
撰寫calltf,讀取Verilog傳給C的參數。
由於compiletf已經驗證了參數的正確性,calltf可大膽的假設參數都已經正確。
52行
C所抓到的Verilog參數值,為一個struct。
54行
systf_handle = vpi_handle(vpiSysTfCall, NULL);
// obtain hadle to system task argument
// compiletf has already verified only 1 arg with corret type
arg_iterator = vpi_iterate(vpiArgument, systf_handle);
net_handle = vpi_scan(arg_iterator);
vpi_free_object(arg_iterator);
// read current value
current_value.format = vpiHexStrVal; // read a value as a string
vpi_get_value(net_handle, ¤t_value);
systf_handle -> arg_iterator -> net_handle -> current_value
66行
vpi_printf("has the value %s\n", current_value.value.str);
顯示參數值,vpi_get_str()顯示reg或wire的完整hierarchy架構名稱,並以16進位字串顯示。
Step 3:
建立C function與Verilog system task的連結資料。
s_vpi_systf_data tf_data;
tf_data.type = vpiSysTask;
tf_data.tfname = "$show_value";
tf_data.calltf = show_value_calltf;
tf_data.compiletf = show_value_compiletf;
vpi_register_systf(&tf_data);
}
與Hello World的差別是,多指定了compiletf部分。
Step 4:
一個簡單的Verilog counter
counter.v / Verilog
2
3 module Counter (
4 input iCLK,
5 input iRST_N,
6 output reg [7:0] oCnt
7 );
8
9 reg [7:0] cnt;
10
11 always@(posedge iCLK, negedge iRST_N) begin
12 if (!iRST_N)
13 oCnt <= 8'h00;
14 else
15 oCnt = oCnt + 1'b1;
16 end
17
18 endmodule
Step 5:
Testbench中使用$show_value
counter_tb.v / Verilog
2
3 module Counter_tb;
4
5 reg clk;
6 reg rst_n;
7 wire [7:0] cnt;
8
9 initial begin
10 clk = 0;
11 rst_n = 0;
12
13 #1
14 rst_n = 1;
15
16 #10
17 $show_value(cnt);
18 $display("$display for cnt = %h", cnt);
19
20 #10
21 $show_value(cnt);
22 $display("$display for cnt = %h", cnt);
23
24 #10 $finish;
25 end
26
27 always #5 clk = ~clk;
28
29 Counter counter (
30 .iCLK(clk),
31 .iRST_N(rst_n),
32 .oCnt(cnt)
33 );
34
35 endmodule
Step 6:
使用NC-Verilog模擬
TOOL: ncverilog 05.40-s011: Started on Mar 25, 2009 at 16:53:38 台北標準時間
ncverilog
+access+r
Counter.v
Counter_tb.v
Recompiling reason: file '.\Counter_tb.v' is newer than expected.
expected: Wed Mar 25 16:53:17 2009
actual: Wed Mar 25 16:53:34 2009
file: Counter.v
file: Counter_tb.v
module worklib.Counter_tb:v
errors: 0, warnings: 0
Caching library 'worklib' . Done
Elaborating the design hierarchy:
Building instance overlay tables: .. Done
Generating native compiled code:
worklib.Counter_tb:v <0x24a5152d>
streams: 2, words: 1787
Loading native compiled code: .. Done
Building instance specific data structures.
Design hierarchy summary:
Instances Unique
Modules: 2 2
Registers: 4 4
Scalar wires: 2 -
Vectored wires: 1 -
Always blocks: 2 2
Initial blocks: 1 1
Simulation timescale: 1ns
Writing initial simulation snapshot: worklib.Counter_tb:v
Loading snapshot worklib.Counter_tb:v .. Done
ncsim> source "C:/Program Files/Cadence Design Systems/IUS/tools/inca/files/ncsimrc"
ncsim> run
Signal Counter_tb.cnt has the value 01
$display for cnt = 01
Signal Counter_tb.cnt has the value 02
$display for cnt = 02
Simulation complete via $finish(1) at time 31 NS + 0
.\Counter_tb.v:24 #10 $finish;
ncsim> exit
TOOL: ncverilog 05.40-s011: Exiting on Mar 25, 2009 at 16:53:41 台北標準時間 (total: 00:00:03)
完整程式碼下載
pli_show_value.7z
Reference
The Verilog PLI Handbook Ch.1