(原創) 如何使用Verilog實現split()? (SOC) (Verilog PLI)
Abstract
嚴格來說,並不是使用Verilog實現split(),而是借由Verilog PLI,在Verilog能夠"使用"split(),將string轉成array。
Introduction
使用環境:Visual C++ 6.0 + NC-Verilog 5.4
在(筆記) 如何使用C語言實現split()? (C/C++) (C) (JavaScript)中,我們已經可以在C語言中實現split(),那在Verilog呢?Verilog並不擅長string方面的處理,既然C語言可以實現,借由Verilog PLI,在Verilog也能有split()的功能。
split_vpi.c / C
1 /*
2 (C) OOMusou 2009 http://oomusou.cnblogs.com
3
4 Filename : split_vpi.c
5 Compiler : VC++ 6.0
6 Description : $split() for Verilog
7 Release : 05/09/2009 1.0
8 */
9
10 #include <stdlib.h> // NULL, malloc(), free()
11 #include <string.h> // strcpy(), strtok()
12 #include <stdio.h>
13 #include "vpi_user.h"
14
15 // ltrim function
16 char *ltrim (char *string, char junk) {
17 char* original = string;
18 char *p = original;
19 int trimmed = 0;
20 do {
21 if (*original != junk || trimmed) {
22 trimmed = 1;
23 *p++ = *original;
24 }
25 } while (*original++ != '\0');
26
27 return string;
28 }
29
30 // sizetf routine
31 PLI_INT32 split_sizetf(PLI_BYTE8 *user_data) {
32 return (32); // $split() returns 32-bit value
33 }
34
35 // compiletf routine
36 PLI_INT32 split_compiletf(PLI_BYTE8 *user_data) {
37 vpiHandle systf_handle, arg_iterator, arg_handle;
38 PLI_INT32 arg_type;
39 int err_flag = 0;
40
41 do { // group all tests, so can break out of group on error
42 // obtain a handle to the system task instance
43 systf_handle = vpi_handle(vpiSysTfCall, NULL);
44 if (systf_handle == NULL) {
45 vpi_printf("ERROR: $split failed to obtain systf handle\n");
46 err_flag = 1;
47 break;
48 }
49
50 // obtain iterator to the system task argument
51 arg_iterator = vpi_iterate(vpiArgument, systf_handle);
52 if (arg_iterator == NULL) {
53 vpi_printf("ERROR: $split requires 3 arguments; has none\n");
54 err_flag = 1;
55 break;
56 }
57
58 // check the type of 1st argument
59 arg_handle = vpi_scan(arg_iterator);
60 arg_type = vpi_get(vpiType, arg_handle);
61 if ((arg_type != vpiMemory)) {
62 vpi_printf("ERROR: $split arg1 must be memory or variable array\n");
63 err_flag = 1;
64 break;
65 }
66
67 // obtain a handle of 2nd argument
68 arg_handle = vpi_scan(arg_iterator);
69 if (arg_handle == NULL) {
70 vpi_printf("ERROR: $split requires 2nd argument\n");
71 err_flag = 1;
72 break;
73 }
74
75 // check the type of 2nd argument
76 arg_type = vpi_get(vpiType, arg_handle);
77 if ((arg_type != vpiReg) && (arg_type != vpiNet) && (arg_type != vpiConstant)) {
78 vpi_printf("ERROR: $split arg2 must be reg or net or constant\n");
79 err_flag = 1;
80 break;
81 }
82
83 // obtain a handle of 3rd argument
84 arg_handle = vpi_scan(arg_iterator);
85 if (arg_handle == NULL) {
86 vpi_printf("ERROR: $split requires 3rd argument\n");
87 err_flag = 1;
88 break;
89 }
90
91 // check the type of 3nd argument
92 arg_type = vpi_get(vpiType, arg_handle);
93 if ((arg_type != vpiReg) && (arg_type != vpiNet) && (arg_type != vpiConstant)) {
94 vpi_printf("ERROR: $split arg3 must be reg or net or constant\n");
95 err_flag = 1;
96 break;
97 }
98
99 // check too many argument
100 arg_handle = vpi_scan(arg_iterator);
101 if (arg_handle != NULL) {
102 vpi_printf("ERROR: $split requires 3 arguments, has too many\n");
103 vpi_free_object(arg_iterator);
104 err_flag = 1;
105 break;
106 }
107 } while (0); // end of test group, only executed once
108
109
110 if (err_flag)
111 vpi_control(vpiFinish, 1);
112
113 return (0);
114 }
115
116 // calltf routine
117 PLI_INT32 split_calltf(PLI_BYTE8* user_data) {
118 vpiHandle systf_handle, arg_iterator, arg_handle;
119 PLI_BYTE8 *svalue; // string to hold from s_vpi_value
120 PLI_BYTE8 *str; // string to strtok
121 PLI_BYTE8 *delim; // delimiter
122 PLI_BYTE8 *tok; // token for strtok()
123 vpiHandle mem_handle, mem_word_iterator, mem_word_handle;
124 int cnt = 0;
125
126 s_vpi_value value_s;
127 s_vpi_value memory_word_s;
128
129 systf_handle = vpi_handle(vpiSysTfCall, NULL);
130 arg_iterator = vpi_iterate(vpiArgument, systf_handle);
131
132 // read memory from systf arg 1
133 mem_handle = vpi_scan(arg_iterator);
134 // obtain handles to memory word in memory and write current value
135 mem_word_iterator = vpi_iterate(vpiMemoryWord, mem_handle);
136
137 // read orginal string from systf arg 2
138 arg_handle = vpi_scan(arg_iterator);
139 value_s.format = vpiStringVal;
140 vpi_get_value(arg_handle, &value_s);
141 svalue = value_s.value.str;
142 str = malloc(strlen((char *)svalue) + 1);
143 strcpy(str, (char *)svalue);
144 ltrim(str, ' ');
145
146 // read delimiter from systf arg 3
147 arg_handle = vpi_scan(arg_iterator);
148 vpi_free_object(arg_iterator);
149 value_s.format = vpiStringVal;
150 vpi_get_value(arg_handle, &value_s);
151 svalue = value_s.value.str;
152 delim = malloc(strlen((char *)svalue) + 1);
153 strcpy(delim, (char *)svalue);
154
155 memory_word_s.format = vpiDecStrVal;
156 tok = strtok(str, delim);
157 while(tok != NULL) {
158 if ((mem_word_handle = vpi_scan(mem_word_iterator)) != NULL) {
159 memory_word_s.value.str = tok;
160 vpi_put_value(mem_word_handle, &memory_word_s, NULL, vpiNoDelay);
161 }
162
163 tok = strtok(NULL, delim);
164 cnt++;
165 }
166
167 // memory is larger than string,
168 // free iterater by hand to prevent from memory leak
169 mem_word_handle = vpi_scan(mem_word_iterator);
170 if (mem_word_handle != NULL)
171 vpi_free_object(mem_word_iterator);
172
173 // write token count to simulation as return value $split
174 value_s.format = vpiIntVal;
175 value_s.value.integer = cnt;
176 vpi_put_value(systf_handle, &value_s, NULL, vpiNoDelay);
177
178 free(str);
179 free(delim);
180
181 return (0);
182 }
183
184 // register function
185 void split_register() {
186 s_vpi_systf_data tf_data;
187
188 tf_data.type = vpiSysFunc;
189 tf_data.sysfunctype = vpiSizedFunc;
190 tf_data.tfname = "$split";
191 tf_data.calltf = split_calltf;
192 tf_data.compiletf = split_compiletf;
193 tf_data.sizetf = split_sizetf;
194 tf_data.user_data = NULL;
195 vpi_register_systf(&tf_data);
196 }
2 (C) OOMusou 2009 http://oomusou.cnblogs.com
3
4 Filename : split_vpi.c
5 Compiler : VC++ 6.0
6 Description : $split() for Verilog
7 Release : 05/09/2009 1.0
8 */
9
10 #include <stdlib.h> // NULL, malloc(), free()
11 #include <string.h> // strcpy(), strtok()
12 #include <stdio.h>
13 #include "vpi_user.h"
14
15 // ltrim function
16 char *ltrim (char *string, char junk) {
17 char* original = string;
18 char *p = original;
19 int trimmed = 0;
20 do {
21 if (*original != junk || trimmed) {
22 trimmed = 1;
23 *p++ = *original;
24 }
25 } while (*original++ != '\0');
26
27 return string;
28 }
29
30 // sizetf routine
31 PLI_INT32 split_sizetf(PLI_BYTE8 *user_data) {
32 return (32); // $split() returns 32-bit value
33 }
34
35 // compiletf routine
36 PLI_INT32 split_compiletf(PLI_BYTE8 *user_data) {
37 vpiHandle systf_handle, arg_iterator, arg_handle;
38 PLI_INT32 arg_type;
39 int err_flag = 0;
40
41 do { // group all tests, so can break out of group on error
42 // obtain a handle to the system task instance
43 systf_handle = vpi_handle(vpiSysTfCall, NULL);
44 if (systf_handle == NULL) {
45 vpi_printf("ERROR: $split failed to obtain systf handle\n");
46 err_flag = 1;
47 break;
48 }
49
50 // obtain iterator to the system task argument
51 arg_iterator = vpi_iterate(vpiArgument, systf_handle);
52 if (arg_iterator == NULL) {
53 vpi_printf("ERROR: $split requires 3 arguments; has none\n");
54 err_flag = 1;
55 break;
56 }
57
58 // check the type of 1st argument
59 arg_handle = vpi_scan(arg_iterator);
60 arg_type = vpi_get(vpiType, arg_handle);
61 if ((arg_type != vpiMemory)) {
62 vpi_printf("ERROR: $split arg1 must be memory or variable array\n");
63 err_flag = 1;
64 break;
65 }
66
67 // obtain a handle of 2nd argument
68 arg_handle = vpi_scan(arg_iterator);
69 if (arg_handle == NULL) {
70 vpi_printf("ERROR: $split requires 2nd argument\n");
71 err_flag = 1;
72 break;
73 }
74
75 // check the type of 2nd argument
76 arg_type = vpi_get(vpiType, arg_handle);
77 if ((arg_type != vpiReg) && (arg_type != vpiNet) && (arg_type != vpiConstant)) {
78 vpi_printf("ERROR: $split arg2 must be reg or net or constant\n");
79 err_flag = 1;
80 break;
81 }
82
83 // obtain a handle of 3rd argument
84 arg_handle = vpi_scan(arg_iterator);
85 if (arg_handle == NULL) {
86 vpi_printf("ERROR: $split requires 3rd argument\n");
87 err_flag = 1;
88 break;
89 }
90
91 // check the type of 3nd argument
92 arg_type = vpi_get(vpiType, arg_handle);
93 if ((arg_type != vpiReg) && (arg_type != vpiNet) && (arg_type != vpiConstant)) {
94 vpi_printf("ERROR: $split arg3 must be reg or net or constant\n");
95 err_flag = 1;
96 break;
97 }
98
99 // check too many argument
100 arg_handle = vpi_scan(arg_iterator);
101 if (arg_handle != NULL) {
102 vpi_printf("ERROR: $split requires 3 arguments, has too many\n");
103 vpi_free_object(arg_iterator);
104 err_flag = 1;
105 break;
106 }
107 } while (0); // end of test group, only executed once
108
109
110 if (err_flag)
111 vpi_control(vpiFinish, 1);
112
113 return (0);
114 }
115
116 // calltf routine
117 PLI_INT32 split_calltf(PLI_BYTE8* user_data) {
118 vpiHandle systf_handle, arg_iterator, arg_handle;
119 PLI_BYTE8 *svalue; // string to hold from s_vpi_value
120 PLI_BYTE8 *str; // string to strtok
121 PLI_BYTE8 *delim; // delimiter
122 PLI_BYTE8 *tok; // token for strtok()
123 vpiHandle mem_handle, mem_word_iterator, mem_word_handle;
124 int cnt = 0;
125
126 s_vpi_value value_s;
127 s_vpi_value memory_word_s;
128
129 systf_handle = vpi_handle(vpiSysTfCall, NULL);
130 arg_iterator = vpi_iterate(vpiArgument, systf_handle);
131
132 // read memory from systf arg 1
133 mem_handle = vpi_scan(arg_iterator);
134 // obtain handles to memory word in memory and write current value
135 mem_word_iterator = vpi_iterate(vpiMemoryWord, mem_handle);
136
137 // read orginal string from systf arg 2
138 arg_handle = vpi_scan(arg_iterator);
139 value_s.format = vpiStringVal;
140 vpi_get_value(arg_handle, &value_s);
141 svalue = value_s.value.str;
142 str = malloc(strlen((char *)svalue) + 1);
143 strcpy(str, (char *)svalue);
144 ltrim(str, ' ');
145
146 // read delimiter from systf arg 3
147 arg_handle = vpi_scan(arg_iterator);
148 vpi_free_object(arg_iterator);
149 value_s.format = vpiStringVal;
150 vpi_get_value(arg_handle, &value_s);
151 svalue = value_s.value.str;
152 delim = malloc(strlen((char *)svalue) + 1);
153 strcpy(delim, (char *)svalue);
154
155 memory_word_s.format = vpiDecStrVal;
156 tok = strtok(str, delim);
157 while(tok != NULL) {
158 if ((mem_word_handle = vpi_scan(mem_word_iterator)) != NULL) {
159 memory_word_s.value.str = tok;
160 vpi_put_value(mem_word_handle, &memory_word_s, NULL, vpiNoDelay);
161 }
162
163 tok = strtok(NULL, delim);
164 cnt++;
165 }
166
167 // memory is larger than string,
168 // free iterater by hand to prevent from memory leak
169 mem_word_handle = vpi_scan(mem_word_iterator);
170 if (mem_word_handle != NULL)
171 vpi_free_object(mem_word_iterator);
172
173 // write token count to simulation as return value $split
174 value_s.format = vpiIntVal;
175 value_s.value.integer = cnt;
176 vpi_put_value(systf_handle, &value_s, NULL, vpiNoDelay);
177
178 free(str);
179 free(delim);
180
181 return (0);
182 }
183
184 // register function
185 void split_register() {
186 s_vpi_systf_data tf_data;
187
188 tf_data.type = vpiSysFunc;
189 tf_data.sysfunctype = vpiSizedFunc;
190 tf_data.tfname = "$split";
191 tf_data.calltf = split_calltf;
192 tf_data.compiletf = split_compiletf;
193 tf_data.sizetf = split_sizetf;
194 tf_data.user_data = NULL;
195 vpi_register_systf(&tf_data);
196 }
15行
// ltrim function
char *ltrim (char *string, char junk) {
char* original = string;
char *p = original;
int trimmed = 0;
do {
if (*original != junk || trimmed) {
trimmed = 1;
*p++ = *original;
}
} while (*original++ != '\0');
return string;
}
char *ltrim (char *string, char junk) {
char* original = string;
char *p = original;
int trimmed = 0;
do {
if (*original != junk || trimmed) {
trimmed = 1;
*p++ = *original;
}
} while (*original++ != '\0');
return string;
}
的ltrim並非必要,但Verilog的string與C的string有些不同,所以特別補上。在C語言,若string定義比實際string長,char會向左靠攏,並在最後補上\0,但在Verilog卻不是這樣,而是項右靠攏,左側補上空白,所以必須先ltrim清掉左側的空白。
split_tb.v / Verilog
1 /*
2 (C) OOMusou 2009 http://oomusou.cnblogs.com
3
4 Filename : split_tb.v
5 Simulator : NC-Verilog 5.4
6 Description : testbench for $split()
7 Release : 05/09/2009 1.0
8 */
9 `timescale 1ns/1ns
10
11 module split_tb;
12 reg [32*8-1:0] str;
13 reg [1*8-1:0] delim;
14 reg [31:0] mem [0:2];
15
16 integer i;
17 integer tok_cnt;
18
19 initial begin
20 str = " 10,20,30";
21 delim = ",";
22 tok_cnt = $split(mem, str, delim);
23
24 for(i=0; i<tok_cnt; i=i+1)
25 $display("mem[%1d]=%2d", i, mem[i]);
26
27 #50;
28 $finish;
29 end
30 endmodule
2 (C) OOMusou 2009 http://oomusou.cnblogs.com
3
4 Filename : split_tb.v
5 Simulator : NC-Verilog 5.4
6 Description : testbench for $split()
7 Release : 05/09/2009 1.0
8 */
9 `timescale 1ns/1ns
10
11 module split_tb;
12 reg [32*8-1:0] str;
13 reg [1*8-1:0] delim;
14 reg [31:0] mem [0:2];
15
16 integer i;
17 integer tok_cnt;
18
19 initial begin
20 str = " 10,20,30";
21 delim = ",";
22 tok_cnt = $split(mem, str, delim);
23
24 for(i=0; i<tok_cnt; i=i+1)
25 $display("mem[%1d]=%2d", i, mem[i]);
26
27 #50;
28 $finish;
29 end
30 endmodule
執行結果
完整程式碼下載
pli_split.7z