1 /*
2  *  Copyright (C) 2012-2018  Martin Whitaker. (icarus@martin-whitaker.me.uk)
3  *
4  *  This program is free software; you can redistribute it and/or modify
5  *  it under the terms of the GNU General Public License as published by
6  *  the Free Software Foundation; either version 2 of the License, or
7  *  (at your option) any later version.
8  *
9  *  This program is distributed in the hope that it will be useful,
10  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *  GNU General Public License for more details.
13  *
14  *  You should have received a copy of the GNU General Public License along
15  *  with this program; if not, write to the Free Software Foundation, Inc.,
16  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18 
19 #include "sys_priv.h"
20 #include <assert.h>
21 #include <inttypes.h>
22 #include <stdlib.h>
23 #include "ivl_alloc.h"
24 
25 /*
26  * Check to see if an argument is a single bit net.
27  */
check_net_arg(vpiHandle arg,vpiHandle callh,const char * name)28 static void check_net_arg(vpiHandle arg, vpiHandle callh, const char *name)
29 {
30       assert(arg);
31 
32       switch (vpi_get(vpiType, arg)) {
33 	  case vpiPartSelect:
34             if (vpi_get(vpiType, vpi_handle(vpiParent, arg)) != vpiNet)
35                   break;
36 	    // fallthrough
37 	  case vpiNet:
38 	    if (vpi_get(vpiSize, arg) != 1)
39                   break;
40             return;
41 	  default:
42             break;
43       }
44       vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
45                  (int)vpi_get(vpiLineNo, callh));
46       vpi_printf("%s's first argument must be a scalar net or "
47                  "a bit-select of a vector net.\n", name);
48       vpi_control(vpiFinish, 1);
49 }
50 
51 /*
52  * Check to see if an argument is a variable.
53  */
check_var_arg(vpiHandle arg,vpiHandle callh,const char * name,const char * arg_name)54 static void check_var_arg(vpiHandle arg, vpiHandle callh, const char *name,
55                           const char *arg_name)
56 {
57       assert(arg);
58 
59       switch (vpi_get(vpiType, arg)) {
60 	  case vpiPartSelect:
61             if (vpi_get(vpiType, vpi_handle(vpiParent, arg)) == vpiNet)
62                   break;
63 	  case vpiMemoryWord:
64 	  case vpiBitVar:
65 	  case vpiReg:
66 	  case vpiIntegerVar:
67 	  case vpiIntVar:
68 	  case vpiLongIntVar:
69 	  case vpiTimeVar:
70 	    return;
71 	  default:
72             break;
73       }
74       vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
75                  (int)vpi_get(vpiLineNo, callh));
76       vpi_printf("%s's %s argument must be a variable.\n",
77                  name, arg_name);
78       vpi_control(vpiFinish, 1);
79 }
80 
sys_countdrivers_sizetf(ICARUS_VPI_CONST PLI_BYTE8 * name)81 static PLI_INT32 sys_countdrivers_sizetf(ICARUS_VPI_CONST PLI_BYTE8*name)
82 {
83     (void)name;  /* Parameter is not used. */
84     return 1;
85 }
86 
87 /*
88  * Check that the given $countdrivers() call has valid arguments.
89  */
sys_countdrivers_compiletf(ICARUS_VPI_CONST PLI_BYTE8 * name)90 static PLI_INT32 sys_countdrivers_compiletf(ICARUS_VPI_CONST PLI_BYTE8 *name)
91 {
92       vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
93       vpiHandle argv = vpi_iterate(vpiArgument, callh);
94       vpiHandle arg;
95       unsigned arg_num;
96 
97 	/* Check that there are arguments. */
98       if (argv == 0) {
99 	    vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, callh),
100 	               (int)vpi_get(vpiLineNo, callh));
101 	    vpi_printf("%s requires at least one argument.\n", name);
102 	    vpi_control(vpiFinish, 1);
103 	    return 0;
104       }
105 
106 	/* The first argument must be a scalar net or a net bit select. */
107       arg = vpi_scan(argv);
108       check_net_arg(arg, callh, name);
109 
110         /* The optional arguments must be variables. */
111       for (arg_num = 2; arg_num < 7; arg_num += 1) {
112 	    const char *arg_name = NULL;
113 	    switch (arg_num) {
114 	        case 2: arg_name = "second"; break;
115 	        case 3: arg_name = "third";  break;
116 	        case 4: arg_name = "fourth"; break;
117 	        case 5: arg_name = "fifth";  break;
118 	        case 6: arg_name = "sixth";  break;
119 	        default: assert(0);
120 	    }
121 
122 	    arg = vpi_scan(argv);
123             if (arg == 0)
124                   return 0;
125 
126             check_var_arg(arg, callh, name, arg_name);
127       }
128 
129       /* Make sure there are no extra arguments. */
130       check_for_extra_args(argv, callh, name, "six arguments", 0);
131       return 0;
132 }
133 
134 /*
135  * The runtime code for $countdrivers().
136  */
sys_countdrivers_calltf(ICARUS_VPI_CONST PLI_BYTE8 * name)137 static PLI_INT32 sys_countdrivers_calltf(ICARUS_VPI_CONST PLI_BYTE8 *name)
138 {
139       vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
140       vpiHandle argv = vpi_iterate(vpiArgument, callh);
141       vpiHandle arg;
142       unsigned idx;
143       unsigned counts[4];
144       unsigned num_drivers;
145       s_vpi_value val;
146 
147       (void)name;  /* Parameter is not used. */
148 
149         /* All returned values are integers. */
150       val.format = vpiIntVal;
151 
152         /* Get the base net reference and bit select */
153       idx = 0;
154       arg = vpi_scan(argv);
155       assert(arg);
156       if (vpi_get(vpiType, arg) == vpiPartSelect) {
157             idx = vpi_get(vpiLeftRange, arg);
158             arg = vpi_handle(vpiParent, arg);
159             assert(arg);
160       }
161 
162         /* Get the net driver counts from the runtime. */
163       vpip_count_drivers(arg, idx, counts);
164       num_drivers = counts[0] + counts[1] + counts[2];
165 
166         /* Handle optional net_is_forced argument. */
167       arg = vpi_scan(argv);
168       if (arg == 0) goto args_done;
169       val.value.integer = counts[3];
170       vpi_put_value(arg, &val, 0, vpiNoDelay);
171 
172         /* Handle optional number_of_01x_drivers argument. */
173       arg = vpi_scan(argv);
174       if (arg == 0) goto args_done;
175       val.value.integer = num_drivers;
176       vpi_put_value(arg, &val, 0, vpiNoDelay);
177 
178         /* Handle optional number_of_0_drivers argument. */
179       arg = vpi_scan(argv);
180       if (arg == 0) goto args_done;
181       val.value.integer = counts[0];
182       vpi_put_value(arg, &val, 0, vpiNoDelay);
183 
184         /* Handle optional number_of_1_drivers argument. */
185       arg = vpi_scan(argv);
186       if (arg == 0) goto args_done;
187       val.value.integer = counts[1];
188       vpi_put_value(arg, &val, 0, vpiNoDelay);
189 
190         /* Handle optional number_of_x_drivers argument. */
191       arg = vpi_scan(argv);
192       if (arg == 0) goto args_done;
193       val.value.integer = counts[2];
194       vpi_put_value(arg, &val, 0, vpiNoDelay);
195 
196         /* Free the argument iterator. */
197       vpi_free_object(argv);
198 
199 args_done:
200       val.value.integer = (num_drivers > 1) ? 1 : 0;
201       vpi_put_value(callh, &val, 0, vpiNoDelay);
202       return 0;
203 }
204 
205 /*
206  * Routine to register the system tasks/functions provided in this file.
207  */
sys_countdrivers_register(void)208 void sys_countdrivers_register(void)
209 {
210       s_vpi_systf_data tf_data;
211       vpiHandle res;
212 
213       tf_data.type        = vpiSysFunc;
214       tf_data.sysfunctype = vpiSizedFunc;
215       tf_data.tfname      = "$countdrivers";
216       tf_data.calltf      = sys_countdrivers_calltf;
217       tf_data.compiletf   = sys_countdrivers_compiletf;
218       tf_data.sizetf      = sys_countdrivers_sizetf;
219       tf_data.user_data   = "$countdrivers";
220       res = vpi_register_systf(&tf_data);
221       vpip_make_systf_system_defined(res);
222 }
223