1 /*
2 * Copyright (c) 2011-2014 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 "vpi_config.h"
21 # include "vpi_user.h"
22 # include <assert.h>
23 # include "ivl_alloc.h"
24 # include "sys_priv.h"
25
26 /*
27 * The $ivlh_attribute_event implements the VHDL <varname>'event
28 * attribute. It does this by monitoring value-change events on the
29 * operand, and noting the time. If the $ivlh_attribute_event is
30 * called at the same simulation time as a value-change, then the
31 * function returns logic true. Otherwise it returns false.
32 *
33 * $ivlh_{rising,falling}_edge implement the VHDL rising_edge() and
34 * falling_edge() system functions.
35 */
36 struct monitor_data {
37 struct t_vpi_time last_event;
38 struct t_vpi_value last_value;
39 };
40
41 static struct monitor_data **mdata = 0;
42 static unsigned mdata_count = 0;
43
44 typedef enum {
45 EVENT = 0,
46 RISING_EDGE = 1,
47 FALLING_EDGE = 2
48 } event_type_t;
49 static const char* attr_func_names[] = {
50 "$ivlh_attribute_event",
51 "$ivlh_rising_edge",
52 "$ivlh_falling_edge"
53 };
54
55 typedef enum {
56 SHIFT_LEFT = 0,
57 SHIFT_RIGHT = 1,
58 } shift_type_t;
59 static const char* shift_func_names[] = {
60 "$ivlh_shift_left",
61 "$ivlh_shift_right",
62 };
63
64 /* To keep valgrind happy free the allocated memory. */
cleanup_mdata(p_cb_data cause)65 static PLI_INT32 cleanup_mdata(p_cb_data cause)
66 {
67 unsigned idx;
68
69 (void) cause; /* Parameter is not used. */
70
71 for (idx= 0; idx < mdata_count; idx += 1) {
72 free(mdata[idx]);
73 }
74 free(mdata);
75 mdata = 0;
76 mdata_count = 0;
77
78 return 0;
79 }
80
monitor_events(struct t_cb_data * cb)81 static PLI_INT32 monitor_events(struct t_cb_data*cb)
82 {
83 struct monitor_data*mon = (struct monitor_data*)(cb->user_data);
84
85 assert(cb->time);
86 assert(cb->time->type == vpiSimTime);
87
88 mon->last_event = *(cb->time);
89 mon->last_value = *(cb->value);
90
91 return 0;
92 }
93
ivlh_attribute_event_compiletf(ICARUS_VPI_CONST PLI_BYTE8 * data)94 static PLI_INT32 ivlh_attribute_event_compiletf(ICARUS_VPI_CONST PLI_BYTE8*data)
95 {
96 event_type_t type = (event_type_t) data;
97 vpiHandle sys = vpi_handle(vpiSysTfCall, 0);
98 vpiHandle argv = vpi_iterate(vpiArgument, sys);
99 vpiHandle arg;
100 struct monitor_data*mon;
101 struct t_cb_data cb;
102 struct t_vpi_time tb;
103 struct t_vpi_value vb;
104
105 /* Check that there are arguments. */
106 if (argv == 0) {
107 vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, sys),
108 (int)vpi_get(vpiLineNo, sys));
109 vpi_printf("(compiler error) %s requires a single argument.\n",
110 attr_func_names[type]);
111 vpi_control(vpiFinish, 1);
112 return 0;
113 }
114
115 /* Icarus either returns 0 above or has one argument. */
116 arg = vpi_scan(argv);
117 assert(arg);
118
119 mon = malloc(sizeof(struct monitor_data));
120 /* Add this to the list of data. */
121 mdata_count += 1;
122 mdata = (struct monitor_data **) realloc(mdata,
123 sizeof(struct monitor_data **) *
124 mdata_count);
125 mdata[mdata_count-1] = mon;
126
127 tb.type = vpiSimTime;
128 vb.format = vpiScalarVal;
129 cb.reason = cbValueChange;
130 cb.cb_rtn = monitor_events;
131 cb.obj = arg;
132 cb.time = &tb;
133 cb.value = &vb;
134 cb.user_data = (char*) (mon);
135 vpi_register_cb(&cb);
136 vpi_put_userdata(sys, mon);
137
138 /* Check that there is no more than one argument. */
139 arg = vpi_scan(argv);
140 if (arg != 0) {
141 vpi_printf("ERROR: %s:%d: ", vpi_get_str(vpiFile, sys),
142 (int)vpi_get(vpiLineNo, sys));
143 vpi_printf("(compiler error) %s only takes a single argument.\n",
144 attr_func_names[type]);
145 vpi_free_object(argv);
146 vpi_control(vpiFinish, 1);
147 }
148
149 return 0;
150 }
151
ivlh_attribute_event_calltf(ICARUS_VPI_CONST PLI_BYTE8 * data)152 static PLI_INT32 ivlh_attribute_event_calltf(ICARUS_VPI_CONST PLI_BYTE8*data)
153 {
154 event_type_t type = (event_type_t) data;
155 vpiHandle sys = vpi_handle(vpiSysTfCall, 0);
156 struct t_vpi_value rval;
157 struct monitor_data*mon;
158
159 rval.format = vpiScalarVal;
160
161 mon = (struct monitor_data*) (vpi_get_userdata(sys));
162
163 if (mon->last_event.type == 0) {
164 rval.value.scalar = vpi0;
165
166 } else {
167 struct t_vpi_time tnow;
168 tnow.type = vpiSimTime;
169 vpi_get_time(0, &tnow);
170
171 rval.value.scalar = vpi1;
172
173 // Detect if change occurred in this moment
174 if (mon->last_event.high != tnow.high)
175 rval.value.scalar = vpi0;
176 if (mon->last_event.low != tnow.low)
177 rval.value.scalar = vpi0;
178
179 // Determine the edge, if required
180 if (type == RISING_EDGE && mon->last_value.value.scalar != vpi1)
181 rval.value.scalar = vpi0;
182 else if (type == FALLING_EDGE && mon->last_value.value.scalar != vpi0)
183 rval.value.scalar = vpi0;
184 }
185
186 vpi_put_value(sys, &rval, 0, vpiNoDelay);
187
188 return 0;
189 }
190
ivlh_attribute_event_sizetf(ICARUS_VPI_CONST PLI_BYTE8 * type)191 static PLI_INT32 ivlh_attribute_event_sizetf(ICARUS_VPI_CONST PLI_BYTE8*type)
192 {
193 (void) type; /* Parameter is not used. */
194 return 1;
195 }
196
ivlh_shift_calltf(ICARUS_VPI_CONST PLI_BYTE8 * data)197 static PLI_INT32 ivlh_shift_calltf(ICARUS_VPI_CONST PLI_BYTE8*data)
198 {
199 shift_type_t shift_type = (shift_type_t) data;
200 vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
201 vpiHandle argv = vpi_iterate(vpiArgument, callh);
202 vpiHandle argh = vpi_scan(argv);
203 vpiHandle counth = vpi_scan(argv);
204 s_vpi_value val;
205 PLI_INT32 count;
206
207 vpi_free_object(argv);
208
209 val.format = vpiIntVal;
210 vpi_get_value(counth, &val);
211 count = val.value.integer;
212
213 val.format = vpiIntVal;
214 vpi_get_value(argh, &val);
215
216 if(shift_type == SHIFT_LEFT)
217 val.value.integer <<= count;
218 else if(shift_type == SHIFT_RIGHT)
219 val.value.integer >>= count;
220 else
221 assert(0);
222
223 vpi_put_value(callh, &val, 0, vpiNoDelay);
224
225 return 0;
226 }
227
ivlh_shift_sizetf(ICARUS_VPI_CONST PLI_BYTE8 * type)228 static PLI_INT32 ivlh_shift_sizetf(ICARUS_VPI_CONST PLI_BYTE8*type)
229 {
230 (void) type; /* Parameter is not used. */
231 return 32;
232 }
233
vhdl_register(void)234 static void vhdl_register(void)
235 {
236 s_vpi_systf_data tf_data;
237 s_cb_data cb;
238 vpiHandle res;
239
240 /* Event attribute functions */
241 tf_data.type = vpiSysFunc;
242 tf_data.sysfunctype = vpiSizedFunc;
243 tf_data.calltf = ivlh_attribute_event_calltf;
244 tf_data.compiletf = ivlh_attribute_event_compiletf;
245 tf_data.sizetf = ivlh_attribute_event_sizetf;
246 tf_data.tfname = attr_func_names[EVENT];
247 tf_data.user_data = (PLI_BYTE8*) EVENT;
248 res = vpi_register_systf(&tf_data);
249 vpip_make_systf_system_defined(res);
250
251 tf_data.tfname = attr_func_names[RISING_EDGE];
252 tf_data.user_data = (PLI_BYTE8*) RISING_EDGE;
253 res = vpi_register_systf(&tf_data);
254 vpip_make_systf_system_defined(res);
255
256 tf_data.tfname = attr_func_names[FALLING_EDGE];
257 tf_data.user_data = (PLI_BYTE8*) FALLING_EDGE;
258 res = vpi_register_systf(&tf_data);
259 vpip_make_systf_system_defined(res);
260
261 /* Shift functions */
262 tf_data.type = vpiSysFunc;
263 tf_data.sysfunctype = vpiSizedFunc;
264 tf_data.calltf = ivlh_shift_calltf;
265 tf_data.compiletf = sys_two_numeric_args_compiletf;
266 tf_data.sizetf = ivlh_shift_sizetf;
267 tf_data.tfname = shift_func_names[SHIFT_LEFT];
268 tf_data.user_data = (PLI_BYTE8*) SHIFT_LEFT;
269 res = vpi_register_systf(&tf_data);
270 vpip_make_systf_system_defined(res);
271
272 tf_data.tfname = shift_func_names[SHIFT_RIGHT];
273 tf_data.user_data = (PLI_BYTE8*) SHIFT_RIGHT;
274 res = vpi_register_systf(&tf_data);
275 vpip_make_systf_system_defined(res);
276
277 /* Create a callback to clear the monitor data memory when the
278 * simulator finishes. */
279 cb.time = NULL;
280 cb.reason = cbEndOfSimulation;
281 cb.cb_rtn = cleanup_mdata;
282 cb.user_data = NULL;
283 cb.obj = NULL;
284
285 vpi_register_cb(&cb);
286 }
287
288 void (*vlog_startup_routines[])(void) = {
289 vhdl_register,
290 0
291 };
292