1 /*
2 * Copyright (c) 2003-2020 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 /*
21 * This file includes functions for evaluating REAL expressions.
22 */
23 # include "vvp_priv.h"
24 # include <string.h>
25 # include <stdlib.h>
26 # include <math.h>
27 # include <assert.h>
28 # include <stdbool.h>
29
30 static unsigned long word_alloc_mask = 0x0f;
31
allocate_word()32 int allocate_word()
33 {
34 int res = 4;
35
36 while (res < WORD_COUNT && (1U << res) & word_alloc_mask)
37 res += 1;
38
39 if (res >= WORD_COUNT) {
40 fprintf(stderr, "vvp.tgt error: Thread words (%d) exhausted "
41 "during VVP code generation.\n", WORD_COUNT);
42 exit(1);
43 }
44 word_alloc_mask |= 1U << res;
45 return res;
46 }
47
clr_word(int res)48 void clr_word(int res)
49 {
50 assert(res < WORD_COUNT);
51 word_alloc_mask &= ~ (1U << res);
52 }
53
draw_binary_real(ivl_expr_t expr)54 static void draw_binary_real(ivl_expr_t expr)
55 {
56 switch (ivl_expr_opcode(expr)) {
57 case 'E':
58 case 'N':
59 case 'w':
60 case 'W':
61 case 'l':
62 case 'r':
63 case 'R':
64 case '&':
65 case '|':
66 case '^':
67 case 'A':
68 case 'O':
69 case 'X':
70 /* These should be caught in draw_eval_real(). */
71 assert(0);
72 }
73
74 draw_eval_real(ivl_expr_oper1(expr));
75 draw_eval_real(ivl_expr_oper2(expr));
76
77 switch (ivl_expr_opcode(expr)) {
78
79 case '+':
80 fprintf(vvp_out, " %%add/wr;\n");
81 break;
82
83 case '-':
84 fprintf(vvp_out, " %%sub/wr;\n");
85 break;
86
87 case '*':
88 fprintf(vvp_out, " %%mul/wr;\n");
89 break;
90
91 case '/':
92 fprintf(vvp_out, " %%div/wr;\n");
93 break;
94
95 case '%':
96 fprintf(vvp_out, " %%mod/wr;\n");
97 break;
98 case 'p':
99 fprintf(vvp_out, " %%pow/wr;\n");
100 break;
101
102 case 'm':
103 fprintf(vvp_out, " %%min/wr;\n");
104 break;
105
106 case 'M':
107 fprintf(vvp_out, " %%max/wr;\n");
108 break;
109
110 default:
111 fprintf(stderr, "vvp.tgt error: draw_binary_real(%c)\n",
112 ivl_expr_opcode(expr));
113 assert(0);
114 }
115 }
116
draw_number_real(ivl_expr_t expr)117 static void draw_number_real(ivl_expr_t expr)
118 {
119 unsigned int idx;
120 const char*bits = ivl_expr_bits(expr);
121 unsigned wid = ivl_expr_width(expr);
122 unsigned long mant = 0;
123 int vexp = 0x1000;
124
125 /* If this is a negative number, then arrange for the 2's
126 complement to be calculated as we scan through the
127 value. Real values are sign-magnitude, and this negation
128 gets us a magnitude. */
129
130 int negate = 0;
131 int carry = 0;
132 if (ivl_expr_signed(expr) && (bits[wid-1] == '1')) {
133 negate = 1;
134 carry = 1;
135 }
136
137 for (idx = 0 ; idx < wid && idx < IMM_WID ; idx += 1) {
138 int cur_bit = bits[idx] == '1'? 1 : 0;
139
140 if (negate) {
141 cur_bit ^= 1;
142 cur_bit += carry;
143 carry = (cur_bit >> 1) & 1;
144 cur_bit &= 1;
145 }
146
147 if (cur_bit) mant |= 1 << idx;
148 }
149
150 for ( ; idx < wid ; idx += 1) {
151 if (ivl_expr_signed(expr) && (bits[idx] == bits[IMM_WID-1]))
152 continue;
153
154 if (bits[idx] == '0')
155 continue;
156
157 fprintf(stderr, "vvp.tgt error: mantissa doesn't fit!\n");
158 assert(0);
159 }
160
161 /* If required, add in a sign bit. */
162 if (negate)
163 vexp |= 0x4000;
164
165 fprintf(vvp_out, " %%pushi/real %lu, %d; load(num)= %c%lu (wid=%u)\n",
166 mant, vexp, (vexp&0x4000)? '-' : '+', mant, wid);
167 }
168
draw_property_real(ivl_expr_t expr)169 static void draw_property_real(ivl_expr_t expr)
170 {
171 ivl_signal_t sig = ivl_expr_signal(expr);
172 unsigned pidx = ivl_expr_property_idx(expr);
173
174 fprintf(vvp_out, " %%load/obj v%p_0;\n", sig);
175 fprintf(vvp_out, " %%prop/r %u;\n", pidx);
176 fprintf(vvp_out, " %%pop/obj 1, 0;\n");
177 }
178
draw_realnum_real(ivl_expr_t expr)179 static void draw_realnum_real(ivl_expr_t expr)
180 {
181 double value = ivl_expr_dvalue(expr);
182
183 double fract;
184 int expo, vexp;
185 unsigned long mant;
186 int sign = 0;
187
188 /* Handle the special case that the value is +-inf. */
189 if (isinf(value)) {
190 if (value > 0)
191 fprintf(vvp_out, " %%pushi/real 0, %d; load=+inf\n",
192 0x3fff);
193 else
194 fprintf(vvp_out, " %%pushi/real 0, %d; load=-inf\n",
195 0x7fff);
196 return;
197 }
198 /* Handle the special case that the value is NaN. */
199 if (value != value) {
200 fprintf(vvp_out, " %%pushi/real 1, %d; load=NaN\n",
201 0x3fff);
202 return;
203 }
204
205 if (value < 0) {
206 sign = 0x4000;
207 value *= -1;
208 }
209
210 fract = frexp(value, &expo);
211 fract = ldexp(fract, 31);
212 mant = fract;
213 expo -= 31;
214
215 vexp = expo + 0x1000;
216 assert(vexp >= 0);
217 assert(vexp < 0x2000);
218 vexp += sign;
219
220 fprintf(vvp_out, " %%pushi/real %lu, %d; load=%#g\n",
221 mant, vexp, ivl_expr_dvalue(expr));
222
223 /* Capture the residual bits, if there are any. Note that an
224 IEEE754 mantissa has 52 bits, 31 of which were accounted
225 for already. */
226 fract -= floor(fract);
227 fract = ldexp(fract, 22);
228 mant = fract;
229 expo -= 22;
230
231 vexp = expo + 0x1000;
232 assert(vexp >= 0);
233 assert(vexp < 0x2000);
234 vexp += sign;
235
236 if (mant != 0) {
237 fprintf(vvp_out, " %%pushi/real %lu, %d; load=%#g\n",
238 mant, vexp, ivl_expr_dvalue(expr));
239 fprintf(vvp_out, " %%add/wr;\n");
240 }
241 }
242
243 /*
244 * The real value of a logic expression is the integer value of the
245 * expression converted to real.
246 */
draw_real_logic_expr(ivl_expr_t expr)247 static void draw_real_logic_expr(ivl_expr_t expr)
248 {
249 draw_eval_vec4(expr);
250 const char*sign_flag = ivl_expr_signed(expr)? "/s" : "";
251 fprintf(vvp_out, " %%cvt/rv%s;\n", sign_flag);
252 }
253
draw_select_real(ivl_expr_t expr)254 static void draw_select_real(ivl_expr_t expr)
255 {
256 /* The sube references the expression to be selected from. */
257 ivl_expr_t sube = ivl_expr_oper1(expr);
258 /* This is the select expression */
259 ivl_expr_t shift= ivl_expr_oper2(expr);
260
261 /* Assume the sub-expression is a signal */
262 ivl_signal_t sig = ivl_expr_signal(sube);
263 assert(ivl_signal_data_type(sig) == IVL_VT_DARRAY || ivl_signal_data_type(sig) == IVL_VT_QUEUE);
264
265 draw_eval_expr_into_integer(shift, 3);
266 fprintf(vvp_out, " %%load/dar/r v%p_0;\n", sig);
267 }
268
real_ex_pop(ivl_expr_t expr)269 static void real_ex_pop(ivl_expr_t expr)
270 {
271 const char*fb;
272 ivl_expr_t arg;
273
274 if (strcmp(ivl_expr_name(expr), "$ivl_queue_method$pop_back")==0)
275 fb = "b";
276 else
277 fb = "f";
278
279 arg = ivl_expr_parm(expr, 0);
280 assert(ivl_expr_type(arg) == IVL_EX_SIGNAL);
281
282 fprintf(vvp_out, " %%qpop/%s/real v%p_0;\n", fb, ivl_expr_signal(arg));
283 }
284
draw_sfunc_real(ivl_expr_t expr)285 static void draw_sfunc_real(ivl_expr_t expr)
286 {
287 switch (ivl_expr_value(expr)) {
288
289 case IVL_VT_REAL:
290 if (ivl_expr_parms(expr) == 0) {
291 fprintf(vvp_out, " %%vpi_func/r %u %u \"%s\" {0 0 0};\n",
292 ivl_file_table_index(ivl_expr_file(expr)),
293 ivl_expr_lineno(expr), ivl_expr_name(expr));
294
295 } else {
296 draw_vpi_rfunc_call(expr);
297 }
298 break;
299
300 case IVL_VT_VECTOR:
301 /* If the value of the sfunc is a vector, then evaluate
302 it as a vector, then convert the result to a real
303 (via an index register) for the result. */
304 draw_real_logic_expr(expr);
305 break;
306
307 default:
308 assert(0);
309 }
310
311 }
312
draw_signal_real_real(ivl_expr_t expr)313 static void draw_signal_real_real(ivl_expr_t expr)
314 {
315 ivl_signal_t sig = ivl_expr_signal(expr);
316
317 /* Special Case: If the signal is the return value of the function,
318 then use a different opcode to get the value. */
319 if (signal_is_return_value(sig)) {
320 assert(ivl_signal_dimensions(sig) == 0);
321 fprintf(vvp_out, " %%retload/real 0; Load %s (draw_signal_real_real)\n",
322 ivl_signal_basename(sig));
323 return;
324 }
325
326
327 if (ivl_signal_dimensions(sig) == 0) {
328 fprintf(vvp_out, " %%load/real v%p_0;\n", sig);
329 return;
330 }
331
332 ivl_expr_t word_ex = ivl_expr_oper1(expr);
333 int word_ix = allocate_word();
334 draw_eval_expr_into_integer(word_ex, word_ix);
335 fprintf(vvp_out, " %%load/ar v%p, %d;\n", sig, word_ix);
336 clr_word(word_ix);
337 }
338
draw_signal_real(ivl_expr_t expr)339 static void draw_signal_real(ivl_expr_t expr)
340 {
341 ivl_signal_t sig = ivl_expr_signal(expr);
342 switch (ivl_signal_data_type(sig)) {
343 case IVL_VT_LOGIC:
344 draw_real_logic_expr(expr);
345 return;
346 case IVL_VT_REAL:
347 draw_signal_real_real(expr);
348 return;
349 default:
350 fprintf(stderr, "vvp.tgt error: signal_data_type=%d\n",
351 ivl_signal_data_type(sig));
352 assert(0);
353 return;
354 }
355 }
356
357 /* If we have nested ternary operators they are likely tail recursive.
358 * This code is structured to allow this recursion without overflowing
359 * the available thread words. */
draw_ternary_real(ivl_expr_t expr)360 static void draw_ternary_real(ivl_expr_t expr)
361 {
362 ivl_expr_t cond = ivl_expr_oper1(expr);
363 ivl_expr_t true_ex = ivl_expr_oper2(expr);
364 ivl_expr_t false_ex = ivl_expr_oper3(expr);
365
366 unsigned lab_true = local_count++;
367 unsigned lab_out = local_count++;
368
369 int cond_flag = allocate_flag();
370
371 /* Evaluate the ternary condition. */
372 draw_eval_vec4(cond);
373 if (ivl_expr_width(cond) > 1)
374 fprintf(vvp_out, " %%or/r;\n");
375
376 fprintf(vvp_out, " %%flag_set/vec4 %d;\n", cond_flag);
377
378
379 /* Evaluate the true expression second. */
380 fprintf(vvp_out, " %%jmp/1 T_%u.%u, %d;\n",
381 thread_count, lab_true, cond_flag);
382
383 /* Evaluate the false expression. */
384 draw_eval_real(false_ex);
385 fprintf(vvp_out, " %%jmp/0 T_%u.%u, %d; End of false expr.\n",
386 thread_count, lab_out, cond_flag);
387
388 /* If the conditional is undefined then blend the real words. */
389 draw_eval_real(true_ex);
390 fprintf(vvp_out, " %%blend/wr;\n");
391 fprintf(vvp_out, " %%jmp T_%u.%u; End of blend\n",
392 thread_count, lab_out);
393
394 /* Evaluate the true expression. */
395 fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_true);
396 draw_eval_real(true_ex);
397
398 /* This is the out label. */
399 fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_out);
400
401 clr_flag(cond_flag);
402 }
403
increment(ivl_expr_t e,bool pre)404 static void increment(ivl_expr_t e, bool pre)
405 {
406 ivl_signal_t sig = ivl_expr_signal(e);
407 fprintf(vvp_out, " %%load/real v%p_0;\n", sig);
408 if (!pre) fprintf(vvp_out, " %%dup/real;\n");
409 fprintf(vvp_out, " %%pushi/real 1, 0x1000;\n");
410 fprintf(vvp_out, " %%add/wr;\n");
411 if ( pre) fprintf(vvp_out, " %%dup/real;\n");
412 fprintf(vvp_out, " %%store/real v%p_0;\n", sig);
413 }
414
decrement(ivl_expr_t e,bool pre)415 static void decrement(ivl_expr_t e, bool pre)
416 {
417 ivl_signal_t sig = ivl_expr_signal(e);
418 fprintf(vvp_out, " %%load/real v%p_0;\n", sig);
419 if (!pre) fprintf(vvp_out, " %%dup/real;\n");
420 fprintf(vvp_out, " %%pushi/real 1, 0x1000;\n");
421 fprintf(vvp_out, " %%sub/wr;\n");
422 if ( pre) fprintf(vvp_out, " %%dup/real;\n");
423 fprintf(vvp_out, " %%store/real v%p_0;\n", sig);
424 }
425
draw_unary_real(ivl_expr_t expr)426 static void draw_unary_real(ivl_expr_t expr)
427 {
428 ivl_expr_t sube;
429
430 /* If the opcode is a ~ or a ! then the sub expression must not be
431 * a real expression, so use vector evaluation and then convert
432 * that result to a real value. */
433 if ((ivl_expr_opcode(expr) == '~') || (ivl_expr_opcode(expr) == '!')) {
434 draw_real_logic_expr(expr);
435 return;
436 }
437
438 sube = ivl_expr_oper1(expr);
439
440 if (ivl_expr_opcode(expr) == 'r') { /* Cast an integer value to a real. */
441 const char *suffix = "";
442 assert(ivl_expr_value(sube) != IVL_VT_REAL);
443 draw_eval_vec4(sube);
444 if (ivl_expr_signed(sube)) suffix = "/s";
445 fprintf(vvp_out, " %%cvt/rv%s;\n", suffix);
446 return;
447 }
448
449
450 if (ivl_expr_opcode(expr) == '+') {
451 draw_eval_real(sube);
452 return;
453 }
454
455 if (ivl_expr_opcode(expr) == '-') {
456 fprintf(vvp_out, " %%pushi/real 0, 0; load 0.0\n");
457 draw_eval_real(sube);
458 fprintf(vvp_out, " %%sub/wr;\n");
459 return;
460 }
461
462 if (ivl_expr_opcode(expr) == 'm') { /* abs() */
463 draw_eval_real(sube);
464 fprintf(vvp_out, " %%abs/wr;\n");
465 return;
466 }
467
468 if (ivl_expr_opcode(expr) == 'v') { /* Handled in eval_expr.c. */
469 fprintf(stderr, "vvp.tgt error: real -> integer cast in real "
470 "context.\n");
471 assert(0);
472 }
473
474 switch (ivl_expr_opcode(expr)) {
475 case 'I':
476 increment(sube, true);
477 return;
478 case 'i':
479 increment(sube, false);
480 return;
481
482 case 'D':
483 decrement(sube, true);
484 return;
485 case 'd':
486 decrement(sube, false);
487 return;
488 }
489
490 fprintf(stderr, "vvp.tgt error: unhandled real unary operator: %c.\n",
491 ivl_expr_opcode(expr));
492 assert(0);
493 }
494
draw_eval_real(ivl_expr_t expr)495 void draw_eval_real(ivl_expr_t expr)
496 {
497
498 /* If this expression/sub-expression is not real then we need
499 * to evaluate it as a bit value and then convert the bit based
500 * result to a real value. This is required to get integer
501 * division to work correctly. */
502 if (ivl_expr_value(expr) != IVL_VT_REAL) {
503 draw_real_logic_expr(expr);
504 return;
505 }
506
507 switch (ivl_expr_type(expr)) {
508
509 case IVL_EX_BINARY:
510 draw_binary_real(expr);
511 break;
512
513 case IVL_EX_NUMBER:
514 draw_number_real(expr);
515 break;
516
517 case IVL_EX_PROPERTY:
518 draw_property_real(expr);
519 break;
520
521 case IVL_EX_REALNUM:
522 draw_realnum_real(expr);
523 break;
524
525 case IVL_EX_SELECT:
526 draw_select_real(expr);
527 break;
528
529 case IVL_EX_SFUNC:
530 if (strcmp(ivl_expr_name(expr), "$ivl_queue_method$pop_back")==0)
531 real_ex_pop(expr);
532 else if (strcmp(ivl_expr_name(expr), "$ivl_queue_method$pop_front")==0)
533 real_ex_pop(expr);
534 else
535 draw_sfunc_real(expr);
536 break;
537
538 case IVL_EX_SIGNAL:
539 draw_signal_real(expr);
540 break;
541
542 case IVL_EX_TERNARY:
543 draw_ternary_real(expr);
544 break;
545
546 case IVL_EX_UFUNC:
547 draw_ufunc_real(expr);
548 break;
549
550 case IVL_EX_UNARY:
551 draw_unary_real(expr);
552 break;
553
554 default:
555 if (ivl_expr_value(expr) == IVL_VT_VECTOR) {
556 draw_eval_vec4(expr);
557 const char*sign_flag = ivl_expr_signed(expr)? "/s" : "";
558 fprintf(vvp_out, " %%cvt/rv%s;\n", sign_flag);
559
560 } else {
561 fprintf(stderr, "vvp.tgt error: XXXX Evaluate real expression (%d)\n",
562 ivl_expr_type(expr));
563 fprintf(vvp_out, " ; XXXX Evaluate real expression (%d)\n",
564 ivl_expr_type(expr));
565 return;
566 }
567 break;
568 }
569
570 }
571