1 /*
2 * Copyright (c) 2001-2019 Stephen Williams (steve@icarus.com)
3 *
4 * This source code is free software; you can redistribute it
5 * and/or modify it in source code form under the terms of the GNU
6 * General Public License as published by the Free Software
7 * Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 */
19
20 # include "config.h"
21 # include "vpi_priv.h"
22 # include "ivl_dlfcn.h"
23 # include "vvp_cleanup.h"
24 # include <cstdio>
25 # include <cstring>
26 # include <sys/types.h>
27 # include <sys/stat.h>
28 # include "ivl_alloc.h"
29
30 static ivl_dll_t*dll_list = 0;
31 static unsigned dll_list_cnt = 0;
32
33 #if defined(__MINGW32__) || defined (__CYGWIN__)
34 typedef PLI_UINT32 (*vpip_set_callback_t)(vpip_routines_s*, PLI_UINT32);
35 #endif
36 typedef void (*vlog_startup_routines_t)(void);
37
38 # define VPIP_MODULE_PATH_MAX 64
39
40 static const char* vpip_module_path[VPIP_MODULE_PATH_MAX] = {0};
41
42 static unsigned vpip_module_path_cnt = 0;
43
44 static bool disable_default_paths = false;
45
vpip_clear_module_paths()46 void vpip_clear_module_paths()
47 {
48 vpip_module_path_cnt = 0;
49 vpip_module_path[0] = 0;
50 disable_default_paths = true;
51 }
52
vpip_add_module_path(const char * path)53 void vpip_add_module_path(const char*path)
54 {
55 if (vpip_module_path_cnt >= VPIP_MODULE_PATH_MAX) {
56 fprintf(stderr, "Too many module paths specified\n");
57 exit(1);
58 }
59 vpip_module_path[vpip_module_path_cnt++] = path;
60 }
61
vpip_add_env_and_default_module_paths()62 void vpip_add_env_and_default_module_paths()
63 {
64 if (disable_default_paths)
65 return;
66
67 if (char *var = ::getenv("IVERILOG_VPI_MODULE_PATH")) {
68 char *ptr = var;
69 char *end = var+strlen(var);
70 int len = 0;
71 while (ptr <= end) {
72 if (*ptr == 0 || *ptr == ':' || *ptr == ';') {
73 if (len > 0) {
74 vpip_add_module_path(strndup(var, len));
75 }
76 len = 0;
77 var = ptr+1;
78 } else {
79 len++;
80 }
81 ptr++;
82 }
83 }
84
85 #ifdef __MINGW32__
86 /* Calculate the module path from the path to the command.
87 This is necessary because of the installation process on
88 Windows. Mostly, it is those darn drive letters, but oh
89 well. We know the command path is formed like this:
90
91 D:\iverilog\bin\iverilog.exe
92
93 The IVL_ROOT in a Windows installation is the path:
94
95 D:\iverilog\lib\ivl$(suffix)
96
97 so we chop the file name and the last directory by
98 turning the last two \ characters to null. Then we append
99 the lib\ivl$(suffix) to finish. */
100 char *s;
101 char basepath[4096], tmp[4096];
102 GetModuleFileName(NULL, tmp, sizeof tmp);
103 /* Convert to a short name to remove any embedded spaces. */
104 GetShortPathName(tmp, basepath, sizeof basepath);
105 s = strrchr(basepath, '\\');
106 if (s) *s = 0;
107 else {
108 fprintf(stderr, "%s: Missing first \\ in exe path!\n", tmp);
109 exit(1);
110 }
111 s = strrchr(basepath, '\\');
112 if (s) *s = 0;
113 else {
114 fprintf(stderr, "%s: Missing second \\ in exe path!\n", tmp);
115 exit(1);
116 }
117 strcat(s, "\\lib\\ivl" IVL_SUFFIX);
118 vpip_add_module_path(strdup(basepath));
119 #else
120 #ifdef MODULE_DIR1
121 vpip_add_module_path(MODULE_DIR1);
122 #endif
123 #endif
124 #ifdef MODULE_DIR2
125 vpip_add_module_path(MODULE_DIR2);
126 #endif
127 }
128
load_module_delete(void)129 void load_module_delete(void)
130 {
131 for (unsigned idx = 0; idx < dll_list_cnt; idx += 1) {
132 ivl_dlclose(dll_list[idx]);
133 }
134 free(dll_list);
135 dll_list = 0;
136 dll_list_cnt = 0;
137 }
138
vpip_load_module(const char * name)139 void vpip_load_module(const char*name)
140 {
141 struct stat sb;
142 int rc;
143 bool export_flag = false;
144 char buf[4096];
145
146 #ifdef __MINGW32__
147 const char sep = '\\';
148 #else
149 const char sep = '/';
150 #endif
151
152 ivl_dll_t dll = 0;
153 buf[0] = 0; /* terminate the string */
154 if (strchr(name, sep)) {
155 /* If the name has at least one directory character in
156 it, then assume it is a complete name, maybe including any
157 possible .vpi suffix. */
158 export_flag = false;
159 rc = stat(name, &sb);
160
161 if (rc != 0) { /* did we find a file? */
162 /* no, try with a .vpi suffix too */
163 export_flag = false;
164 snprintf(buf, sizeof(buf), "%s.vpi", name);
165 rc = stat(buf, &sb);
166
167 /* Try also with the .vpl suffix. */
168 if (rc != 0) {
169 export_flag = true;
170 snprintf(buf, sizeof(buf), "%s.vpl", name);
171 rc = stat(buf, &sb);
172 }
173
174 if (rc != 0) {
175 fprintf(stderr, "%s: Unable to find module file `%s' "
176 "or `%s.vpi'.\n", name,name,buf);
177 return;
178 }
179 } else {
180 strcpy(buf,name); /* yes copy the name into the buffer */
181 }
182
183 } else {
184 rc = -1;
185 for (unsigned idx = 0
186 ; (rc != 0) && (idx < vpip_module_path_cnt)
187 ; idx += 1) {
188 export_flag = false;
189 snprintf(buf, sizeof(buf), "%s%c%s.vpi",
190 vpip_module_path[idx], sep, name);
191 rc = stat(buf,&sb);
192
193 if (rc != 0) {
194 export_flag = true;
195 snprintf(buf, sizeof(buf), "%s%c%s.vpl",
196 vpip_module_path[idx], sep, name);
197 rc = stat(buf,&sb);
198 }
199 }
200
201 if (rc != 0) {
202 fprintf(stderr, "%s: Unable to find a "
203 "`%s.vpi' module on the search path.\n",
204 name, name);
205 return;
206 }
207
208 }
209
210 /* must have found some file that could possibly be a vpi module
211 * try to open it as a shared object.
212 */
213 dll = ivl_dlopen(buf, export_flag);
214 if(dll==0) {
215 /* hmm, this failed, let the user know what has really gone wrong */
216 fprintf(stderr,"%s:`%s' failed to open using dlopen() because:\n"
217 " %s.\n",name,buf,dlerror());
218
219 return;
220 }
221
222 #if defined(__MINGW32__) || defined (__CYGWIN__)
223 void*function = ivl_dlsym(dll, "vpip_set_callback");
224 if (function == 0) {
225 fprintf(stderr, "%s: no vpip_set_callback()\n", name);
226 ivl_dlclose(dll);
227 return;
228 }
229 vpip_set_callback_t set_callback = (vpip_set_callback_t)function;
230 if (!set_callback(&vpi_routines, vpip_routines_version)) {
231 fprintf(stderr, "Failed to link VPI module %s. Try rebuilding it with iverilog-vpi.\n", name);
232 ivl_dlclose(dll);
233 return;
234 }
235 #endif
236
237 void*table = ivl_dlsym(dll, LU "vlog_startup_routines" TU);
238 if (table == 0) {
239 fprintf(stderr, "%s: no vlog_startup_routines\n", name);
240 ivl_dlclose(dll);
241 return;
242 }
243
244 /* Add the dll to the list so it can be closed when we are done. */
245 dll_list_cnt += 1;
246 dll_list = (ivl_dll_t*)realloc(dll_list, dll_list_cnt*sizeof(ivl_dll_t));
247 dll_list[dll_list_cnt-1] = dll;
248
249 vpi_mode_flag = VPI_MODE_REGISTER;
250 vlog_startup_routines_t*routines = (vlog_startup_routines_t*)table;
251 for (unsigned tmp = 0 ; routines[tmp] ; tmp += 1)
252 (routines[tmp])();
253 vpi_mode_flag = VPI_MODE_NONE;
254 }
255