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