Add support for text macros with arguments.
[iverilog.git] / tgt-vvp / vvp_process.c
blobfcc56c7df6be5168090c1f055c780b8b6b0bd34b
1 /*
2 * Copyright (c) 2001-2007 Stephen Williams (steve@icarus.com)
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.
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.
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19 #ifdef HAVE_CVS_IDENT
20 #ident "$Id: vvp_process.c,v 1.133 2007/02/27 05:13:34 steve Exp $"
21 #endif
23 # include "vvp_priv.h"
24 # include <string.h>
25 # include <assert.h>
26 #ifdef HAVE_MALLOC_H
27 # include <malloc.h>
28 #endif
29 # include <stdlib.h>
31 static int show_statement(ivl_statement_t net, ivl_scope_t sscope);
33 unsigned local_count = 0;
34 unsigned thread_count = 0;
36 static unsigned transient_id = 0;
39 * This file includes the code needed to generate VVP code for
40 * processes. Scopes are already declared, we generate here the
41 * executable code for the processes.
44 unsigned bitchar_to_idx(char bit)
46 switch (bit) {
47 case '0':
48 return 0;
49 case '1':
50 return 1;
51 case 'x':
52 return 2;
53 case 'z':
54 return 3;
55 default:
56 assert(0);
57 return 0;
62 * These functions handle the blocking assignment. Use the %set
63 * instruction to perform the actual assignment, and calculate any
64 * lvalues and rvalues that need calculating.
66 * The set_to_lvariable function takes a particular nexus and generates
67 * the %set statements to assign the value.
69 * The show_stmt_assign function looks at the assign statement, scans
70 * the l-values, and matches bits of the r-value with the correct
71 * nexus.
74 static void set_to_lvariable(ivl_lval_t lval,
75 unsigned bit, unsigned wid)
77 ivl_signal_t sig = ivl_lval_sig(lval);
78 ivl_expr_t part_off_ex = ivl_lval_part_off(lval);
79 unsigned part_off = 0;
81 /* Although Verilog doesn't support it, we'll handle
82 here the case of an l-value part select of an array
83 word if the address is constant. */
84 ivl_expr_t word_ix = ivl_lval_idx(lval);
85 unsigned long use_word = 0;
87 if (part_off_ex == 0) {
88 part_off = 0;
89 } else if (number_is_immediate(part_off_ex, 64)) {
90 part_off = get_number_immediate(part_off_ex);
91 part_off_ex = 0;
94 /* If the word index is a constant expression, then evaluate
95 it to select the word, and pay no further heed to the
96 expression itself. */
97 if (word_ix && number_is_immediate(word_ix, 8*sizeof(use_word))) {
98 use_word = get_number_immediate(word_ix);
99 word_ix = 0;
102 if (ivl_lval_mux(lval))
103 part_off_ex = ivl_lval_mux(lval);
105 if (part_off_ex) {
106 unsigned skip_set = transient_id++;
108 /* There is a mux expression, so this must be a write to
109 a bit-select l-val. Presumably, the x0 index register
110 has been loaded wit the result of the evaluated
111 part select base expression. */
112 assert(!word_ix);
114 draw_eval_expr_into_integer(part_off_ex, 0);
115 fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set);
117 fprintf(vvp_out, " %%set/x0 v%p_%lu, %u, %u;\n",
118 sig, use_word, bit, wid);
119 fprintf(vvp_out, "t_%u ;\n", skip_set);
120 /* save_signal width of 0 CLEARS the signal from the
121 lookaside. */
122 save_signal_lookaside(bit, sig, use_word, 0);
124 } else if (part_off>0 || ivl_lval_width(lval)!=ivl_signal_width(sig)) {
125 /* There is no mux expression, but a constant part
126 offset. Load that into index x0 and generate a
127 vector set instruction. */
128 assert(ivl_lval_width(lval) == wid);
130 /* If the word index is a constant, then we can write
131 directly to the word and save the index calculation. */
132 if (word_ix == 0) {
133 fprintf(vvp_out, " %%ix/load 0, %u;\n", part_off);
134 fprintf(vvp_out, " %%set/x0 v%p_%lu, %u, %u;\n",
135 sig, use_word, bit, wid);
137 } else {
138 unsigned skip_set = transient_id++;
139 unsigned index_reg = 3;
140 draw_eval_expr_into_integer(word_ix, index_reg);
141 fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set);
142 fprintf(vvp_out, " %%ix/load 1, %u;\n", part_off);
143 fprintf(vvp_out, " %%set/av v%p, %u, %u;\n",
144 sig, bit, wid);
145 fprintf(vvp_out, "t_%u ;\n", skip_set);
147 /* save_signal width of 0 CLEARS the signal from the
148 lookaside. */
149 save_signal_lookaside(bit, sig, use_word, 0);
151 } else if (ivl_signal_array_count(sig) > 1) {
153 /* If the word index is a constant, then we can write
154 directly to the word and save the index calculation. */
155 if (word_ix == 0) {
156 if (use_word < ivl_signal_array_count(sig)) {
157 fprintf(vvp_out, " %%set/v v%p_%lu, %u, %u;\n",
158 sig, use_word, bit, wid);
159 } else {
160 fprintf(vvp_out, " ; %%set/v v%p_%lu, %u, %u "
161 "OUT OF BOUNDS\n", sig, use_word, bit, wid);
164 } else {
165 unsigned skip_set = transient_id++;
166 unsigned index_reg = 3;
167 draw_eval_expr_into_integer(word_ix, index_reg);
168 fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_set);
169 fprintf(vvp_out, " %%ix/load 1, 0;\n");
170 fprintf(vvp_out, " %%set/av v%p, %u, %u;\n",
171 sig, bit, wid);
172 fprintf(vvp_out, "t_%u ;\n", skip_set);
174 /* save_signal width of 0 CLEARS the signal from the
175 lookaside. */
176 save_signal_lookaside(bit, sig, use_word, 0);
179 } else {
180 save_signal_lookaside(bit, sig, use_word, wid);
181 fprintf(vvp_out, " %%set/v v%p_%lu, %u, %u;\n",
182 sig, use_word, bit, wid);
187 static void assign_to_array_word(ivl_signal_t lsig, ivl_expr_t word_ix,
188 unsigned bit, unsigned delay, unsigned width)
190 unsigned skip_assign = transient_id++;
192 /* Calculate array word index into index register 3 */
193 draw_eval_expr_into_integer(word_ix, 3);
194 /* Skip assignment if word expression is not defined. */
195 fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_assign);
197 /* Store expression width into index word 0 */
198 fprintf(vvp_out, " %%ix/load 0, %u; // word width\n", width);
199 /* Store constant (0) word part select into index 1 */
200 fprintf(vvp_out, " %%ix/load 1, 0;\n");
202 fprintf(vvp_out, " %%assign/av v%p, %u, %u;\n", lsig, delay, bit);
203 fprintf(vvp_out, "t_%u ;\n", skip_assign);
205 clear_expression_lookaside();
208 static void assign_to_lvector(ivl_lval_t lval, unsigned bit,
209 unsigned delay, ivl_expr_t dexp,
210 unsigned width)
212 ivl_signal_t sig = ivl_lval_sig(lval);
213 ivl_expr_t part_off_ex = ivl_lval_part_off(lval);
214 unsigned part_off = 0;
216 ivl_expr_t word_ix = ivl_lval_idx(lval);
217 unsigned long use_word = 0;
219 if (ivl_signal_array_count(sig) > 1) {
220 assert(word_ix);
221 if (! number_is_immediate(word_ix, 8*sizeof(use_word))) {
222 assert(!dexp);
223 assign_to_array_word(sig, word_ix, bit, delay, width);
224 return;
227 use_word = get_number_immediate(word_ix);
230 if (part_off_ex == 0) {
231 part_off = 0;
232 } else if (number_is_immediate(part_off_ex, 64)) {
233 part_off = get_number_immediate(part_off_ex);
234 part_off_ex = 0;
237 if (ivl_lval_mux(lval))
238 part_off_ex = ivl_lval_mux(lval);
240 if (part_off_ex) {
241 unsigned skip_assign = transient_id++;
242 assert(dexp == 0);
243 draw_eval_expr_into_integer(part_off_ex, 1);
244 /* If the index expression has XZ bits, skip the assign. */
245 fprintf(vvp_out, " %%jmp/1 t_%u, 4;\n", skip_assign);
246 fprintf(vvp_out, " %%ix/load 0, %u;\n", width);
247 fprintf(vvp_out, " %%assign/v0/x1 v%p_%lu, %u, %u;\n",
248 sig, use_word, delay, bit);
249 fprintf(vvp_out, "t_%u ;\n", skip_assign);
251 } else if (part_off>0 || ivl_lval_width(lval)!=ivl_signal_width(sig)) {
252 /* There is no mux expression, but a constant part
253 offset. Load that into index x1 and generate a
254 single-bit set instruction. */
255 assert(ivl_lval_width(lval) == width);
257 if (dexp == 0) {
258 /* Constant delay... */
259 fprintf(vvp_out, " %%ix/load 0, %u;\n", width);
260 fprintf(vvp_out, " %%ix/load 1, %u;\n", part_off);
261 fprintf(vvp_out, " %%assign/v0/x1 v%p_%lu, %u, %u;\n",
262 sig, use_word, delay, bit);
264 } else {
265 /* Calculated delay... */
266 int delay_index = allocate_word();
267 draw_eval_expr_into_integer(dexp, delay_index);
268 fprintf(vvp_out, " %%ix/load 0, %u;\n", width);
269 fprintf(vvp_out, " %%ix/load 1, %u;\n", part_off);
270 fprintf(vvp_out, " %%assign/v0/x1/d v%p_%lu, %u, %u;\n",
271 sig, use_word, delay_index, bit);
272 clr_word(delay_index);
275 } else if (dexp != 0) {
276 draw_eval_expr_into_integer(dexp, 1);
277 fprintf(vvp_out, " %%ix/load 0, %u;\n", width);
278 fprintf(vvp_out, " %%assign/v0/d v%p_%lu, 1, %u;\n",
279 sig, use_word, bit);
280 } else {
281 fprintf(vvp_out, " %%ix/load 0, %u;\n", width);
282 fprintf(vvp_out, " %%assign/v0 v%p_%lu, %u, %u;\n",
283 sig, use_word, delay, bit);
289 * This is a private function to generate %set code for the
290 * statement. At this point, the r-value is evaluated and stored in
291 * the res vector, I just need to generate the %set statements for the
292 * l-values of the assignment.
294 static void set_vec_to_lval(ivl_statement_t net, struct vector_info res)
296 ivl_lval_t lval;
298 unsigned wid = res.wid;
299 unsigned lidx;
300 unsigned cur_rbit = 0;
302 for (lidx = 0 ; lidx < ivl_stmt_lvals(net) ; lidx += 1) {
303 unsigned bidx;
304 unsigned bit_limit = wid - cur_rbit;
306 lval = ivl_stmt_lval(net, lidx);
308 /* Reduce bit_limit to the width of this l-value. */
309 if (bit_limit > ivl_lval_width(lval))
310 bit_limit = ivl_lval_width(lval);
312 /* This is the address within the larger r-value of the
313 bit that this l-value takes. */
314 bidx = res.base < 4? res.base : (res.base+cur_rbit);
316 set_to_lvariable(lval, bidx, bit_limit);
318 /* Now we've consumed this many r-value bits for the
319 current l-value. */
320 cur_rbit += bit_limit;
324 static int show_stmt_assign_vector(ivl_statement_t net)
326 ivl_expr_t rval = ivl_stmt_rval(net);
328 /* Handle the special case that the expression is a real
329 value. Evaluate the real expression, then convert the
330 result to a vector. Then store that vector into the
331 l-value. */
332 if (ivl_expr_value(rval) == IVL_VT_REAL) {
333 int word = draw_eval_real(rval);
334 /* This is the accumulated with of the l-value of the
335 assignment. */
336 unsigned wid = ivl_stmt_lwidth(net);
338 struct vector_info vec;
340 vec.base = allocate_vector(wid);
341 vec.wid = wid;
343 fprintf(vvp_out, " %%cvt/vr %u, %d, %u;\n",
344 vec.base, word, vec.wid);
346 clr_word(word);
348 set_vec_to_lval(net, vec);
350 clr_vector(vec);
351 return 0;
355 { struct vector_info res = draw_eval_expr(rval, 0);
356 set_vec_to_lval(net, res);
357 if (res.base > 3)
358 clr_vector(res);
362 return 0;
366 * This function assigns a value to a real .variable. This is destined
367 * for /dev/null when typed ivl_signal_t takes over all the real
368 * variable support.
370 static int show_stmt_assign_sig_real(ivl_statement_t net)
372 int res;
373 ivl_lval_t lval;
374 ivl_signal_t var;
376 res = draw_eval_real(ivl_stmt_rval(net));
377 clr_word(res);
379 assert(ivl_stmt_lvals(net) == 1);
380 lval = ivl_stmt_lval(net, 0);
381 var = ivl_lval_sig(lval);
382 assert(var != 0);
384 assert(ivl_signal_array_count(var) == 1);
386 fprintf(vvp_out, " %%set/wr v%p_0, %d;\n", var, res);
388 return 0;
391 static int show_stmt_assign(ivl_statement_t net)
393 ivl_lval_t lval;
394 ivl_signal_t sig;
396 lval = ivl_stmt_lval(net, 0);
398 sig = ivl_lval_sig(lval);
399 if (sig) switch (ivl_signal_data_type(sig)) {
401 case IVL_VT_REAL:
402 return show_stmt_assign_sig_real(net);
404 default:
405 return show_stmt_assign_vector(net);
407 } else {
408 return show_stmt_assign_vector(net);
411 return 0;
415 * This function handles the case of non-blocking assign to word
416 * variables such as real, i.e:
418 * real foo;
419 * foo <= 1.0;
421 * In this case we know (by Verilog syntax) that there is only exactly
422 * 1 l-value, the target identifier, so it should be relatively easy.
424 static int show_stmt_assign_nb_real(ivl_statement_t net)
426 ivl_lval_t lval;
427 ivl_signal_t sig;
428 ivl_expr_t rval = ivl_stmt_rval(net);
429 ivl_expr_t del = ivl_stmt_delay_expr(net);
430 /* variables for the selection of word from an array. */
431 ivl_expr_t word_ix;
432 unsigned long use_word = 0;
433 /* thread address for a word value. */
434 int word;
435 unsigned long delay;
437 /* Must be exactly 1 l-value. */
438 assert(ivl_stmt_lvals(net) == 1);
440 lval = ivl_stmt_lval(net, 0);
441 sig = ivl_lval_sig(lval);
442 assert(sig);
444 if (ivl_signal_array_count(sig) > 1) {
445 word_ix = ivl_lval_idx(lval);
446 assert(word_ix);
447 assert(number_is_immediate(word_ix, 8*sizeof(use_word)));
448 use_word = get_number_immediate(word_ix);
451 delay = 0;
452 if (del && (ivl_expr_type(del) == IVL_EX_ULONG)) {
453 delay = ivl_expr_uvalue(del);
454 del = 0;
457 /* XXXX For now, presume delays are constant. */
458 assert(del == 0);
460 /* Evaluate the r-value */
461 word = draw_eval_real(rval);
463 fprintf(vvp_out, " %%assign/wr v%p_%lu, %lu, %u;\n",
464 sig, use_word, delay, word);
466 clr_word(word);
468 return 0;
471 static int show_stmt_assign_nb(ivl_statement_t net)
473 ivl_lval_t lval;
474 ivl_expr_t rval = ivl_stmt_rval(net);
475 ivl_expr_t del = ivl_stmt_delay_expr(net);
476 ivl_signal_t sig;
478 unsigned long delay = 0;
480 /* Detect special cases that are handled elsewhere. */
481 lval = ivl_stmt_lval(net,0);
482 if ((sig = ivl_lval_sig(lval))) {
483 switch (ivl_signal_data_type(sig)) {
484 case IVL_VT_REAL:
485 return show_stmt_assign_nb_real(net);
486 default:
487 break;
491 if (del && (ivl_expr_type(del) == IVL_EX_ULONG)) {
492 delay = ivl_expr_uvalue(del);
493 del = 0;
497 { struct vector_info res = draw_eval_expr(rval, 0);
498 unsigned wid = res.wid;
499 unsigned lidx;
500 unsigned cur_rbit = 0;
502 for (lidx = 0 ; lidx < ivl_stmt_lvals(net) ; lidx += 1) {
503 unsigned bit_limit = wid - cur_rbit;
504 lval = ivl_stmt_lval(net, lidx);
506 if (bit_limit > ivl_lval_width(lval))
507 bit_limit = ivl_lval_width(lval);
509 unsigned bidx;
511 bidx = res.base < 4? res.base : (res.base+cur_rbit);
512 assign_to_lvector(lval, bidx, delay, del, bit_limit);
514 cur_rbit += bit_limit;
518 if (res.base > 3)
519 clr_vector(res);
522 return 0;
525 static int show_stmt_block(ivl_statement_t net, ivl_scope_t sscope)
527 int rc = 0;
528 unsigned idx;
529 unsigned cnt = ivl_stmt_block_count(net);
531 for (idx = 0 ; idx < cnt ; idx += 1) {
532 rc += show_statement(ivl_stmt_block_stmt(net, idx), sscope);
535 return rc;
539 * This draws an invocation of a named block. This is a little
540 * different because a subscope is created. We do that by creating
541 * a thread to deal with this.
543 static int show_stmt_block_named(ivl_statement_t net, ivl_scope_t scope)
545 int rc;
546 int out_id, sub_id;
547 ivl_scope_t subscope = ivl_stmt_block_scope(net);
549 out_id = transient_id++;
550 sub_id = transient_id++;
552 fprintf(vvp_out, " %%fork t_%u, S_%p;\n",
553 sub_id, subscope);
554 fprintf(vvp_out, " %%jmp t_%u;\n", out_id);
555 fprintf(vvp_out, "t_%u ;\n", sub_id);
557 /* The statement within the fork is in a new thread, so no
558 expression lookaside is valid. */
559 clear_expression_lookaside();
561 rc = show_stmt_block(net, subscope);
562 fprintf(vvp_out, " %%end;\n");
564 fprintf(vvp_out, "t_%u %%join;\n", out_id);
565 clear_expression_lookaside();
567 return rc;
571 static int show_stmt_case(ivl_statement_t net, ivl_scope_t sscope)
573 ivl_expr_t exp = ivl_stmt_cond_expr(net);
574 struct vector_info cond = draw_eval_expr(exp, 0);
575 unsigned count = ivl_stmt_case_count(net);
577 unsigned local_base = local_count;
579 unsigned idx, default_case;
581 local_count += count + 1;
583 /* First draw the branch table. All the non-default cases
584 generate a branch out of here, to the code that implements
585 the case. The default will fall through all the tests. */
586 default_case = count;
588 for (idx = 0 ; idx < count ; idx += 1) {
589 ivl_expr_t cex = ivl_stmt_case_expr(net, idx);
590 struct vector_info cvec;
592 if (cex == 0) {
593 default_case = idx;
594 continue;
597 /* Is the guard expression something I can pass to a
598 %cmpi/u instruction? If so, use that instead. */
600 if ((ivl_statement_type(net) == IVL_ST_CASE)
601 && (ivl_expr_type(cex) == IVL_EX_NUMBER)
602 && (! number_is_unknown(cex))
603 && number_is_immediate(cex, 16)) {
605 unsigned long imm = get_number_immediate(cex);
607 fprintf(vvp_out, " %%cmpi/u %u, %lu, %u;\n",
608 cond.base, imm, cond.wid);
609 fprintf(vvp_out, " %%jmp/1 T_%d.%d, 6;\n",
610 thread_count, local_base+idx);
612 continue;
615 /* Oh well, do this case the hard way. */
617 cvec = draw_eval_expr_wid(cex, cond.wid, STUFF_OK_RO);
618 assert(cvec.wid == cond.wid);
620 switch (ivl_statement_type(net)) {
622 case IVL_ST_CASE:
623 fprintf(vvp_out, " %%cmp/u %u, %u, %u;\n",
624 cond.base, cvec.base, cond.wid);
625 fprintf(vvp_out, " %%jmp/1 T_%d.%d, 6;\n",
626 thread_count, local_base+idx);
627 break;
629 case IVL_ST_CASEX:
630 fprintf(vvp_out, " %%cmp/x %u, %u, %u;\n",
631 cond.base, cvec.base, cond.wid);
632 fprintf(vvp_out, " %%jmp/1 T_%d.%d, 4;\n",
633 thread_count, local_base+idx);
634 break;
636 case IVL_ST_CASEZ:
637 fprintf(vvp_out, " %%cmp/z %u, %u, %u;\n",
638 cond.base, cvec.base, cond.wid);
639 fprintf(vvp_out, " %%jmp/1 T_%d.%d, 4;\n",
640 thread_count, local_base+idx);
641 break;
643 default:
644 assert(0);
647 /* Done with the case expression */
648 clr_vector(cvec);
651 /* Done with the condition expression */
652 clr_vector(cond);
654 /* Emit code for the default case. */
655 if (default_case < count) {
656 ivl_statement_t cst = ivl_stmt_case_stmt(net, default_case);
657 show_statement(cst, sscope);
660 /* Jump to the out of the case. */
661 fprintf(vvp_out, " %%jmp T_%d.%d;\n", thread_count,
662 local_base+count);
664 for (idx = 0 ; idx < count ; idx += 1) {
665 ivl_statement_t cst = ivl_stmt_case_stmt(net, idx);
667 if (idx == default_case)
668 continue;
670 fprintf(vvp_out, "T_%d.%d ;\n", thread_count, local_base+idx);
671 clear_expression_lookaside();
672 show_statement(cst, sscope);
674 fprintf(vvp_out, " %%jmp T_%d.%d;\n", thread_count,
675 local_base+count);
680 /* The out of the case. */
681 fprintf(vvp_out, "T_%d.%d ;\n", thread_count, local_base+count);
682 clear_expression_lookaside();
684 return 0;
687 static int show_stmt_case_r(ivl_statement_t net, ivl_scope_t sscope)
689 ivl_expr_t exp = ivl_stmt_cond_expr(net);
690 int cond = draw_eval_real(exp);
691 unsigned count = ivl_stmt_case_count(net);
693 unsigned local_base = local_count;
695 unsigned idx, default_case;
697 local_count += count + 1;
700 /* First draw the branch table. All the non-default cases
701 generate a branch out of here, to the code that implements
702 the case. The default will fall through all the tests. */
703 default_case = count;
705 for (idx = 0 ; idx < count ; idx += 1) {
706 ivl_expr_t cex = ivl_stmt_case_expr(net, idx);
707 int cvec;
709 if (cex == 0) {
710 default_case = idx;
711 continue;
714 cvec = draw_eval_real(cex);
716 fprintf(vvp_out, " %%cmp/wr %d, %d;\n", cond, cvec);
717 fprintf(vvp_out, " %%jmp/1 T_%d.%d, 4;\n",
718 thread_count, local_base+idx);
720 /* Done with the guard expression value. */
721 clr_word(cvec);
724 /* Done with the case expression. */
725 clr_word(cond);
727 /* Emit code for the case default. The above jump table will
728 fall through to this statement. */
729 if (default_case < count) {
730 ivl_statement_t cst = ivl_stmt_case_stmt(net, default_case);
731 show_statement(cst, sscope);
734 /* Jump to the out of the case. */
735 fprintf(vvp_out, " %%jmp T_%d.%d;\n", thread_count,
736 local_base+count);
738 for (idx = 0 ; idx < count ; idx += 1) {
739 ivl_statement_t cst = ivl_stmt_case_stmt(net, idx);
741 if (idx == default_case)
742 continue;
744 fprintf(vvp_out, "T_%d.%d ;\n", thread_count, local_base+idx);
745 clear_expression_lookaside();
746 show_statement(cst, sscope);
748 fprintf(vvp_out, " %%jmp T_%d.%d;\n", thread_count,
749 local_base+count);
754 /* The out of the case. */
755 fprintf(vvp_out, "T_%d.%d ;\n", thread_count, local_base+count);
757 return 0;
760 static void force_vector_to_lval(ivl_statement_t net, struct vector_info rvec)
762 unsigned lidx;
763 unsigned roff = 0;
765 const char*command_name;
766 const char*command_name_x0;
768 switch (ivl_statement_type(net)) {
769 case IVL_ST_CASSIGN:
770 command_name = "%cassign/v";
771 command_name_x0 = "ERROR";
772 break;
773 case IVL_ST_FORCE:
774 command_name = "%force/v";
775 command_name_x0 = "%force/x0";
776 break;
777 default:
778 command_name = "ERROR";
779 assert(0);
780 break;
783 for (lidx = 0 ; lidx < ivl_stmt_lvals(net) ; lidx += 1) {
784 ivl_lval_t lval = ivl_stmt_lval(net, lidx);
785 ivl_signal_t lsig = ivl_lval_sig(lval);
787 unsigned use_wid = ivl_lval_width(lval);
788 ivl_expr_t part_off_ex = ivl_lval_part_off(lval);
789 unsigned part_off;
790 ivl_expr_t word_idx = ivl_lval_idx(lval);
791 unsigned long use_word = 0;
793 if (part_off_ex == 0) {
794 part_off = 0;
795 } else {
796 assert(number_is_immediate(part_off_ex, 64));
797 part_off = get_number_immediate(part_off_ex);
800 if (word_idx != 0) {
801 assert(number_is_immediate(word_idx, 8*sizeof(unsigned long)));
802 use_word = get_number_immediate(word_idx);
805 /* L-Value must be a signal: reg or wire */
806 assert(lsig != 0);
808 if (part_off != 0 || use_wid != ivl_signal_width(lsig)) {
810 command_name = command_name_x0;
811 fprintf(vvp_out, " %%ix/load 0, %u;\n", part_off);
813 } else {
814 /* Do not support bit or part selects of l-values yet. */
815 assert(ivl_lval_mux(lval) == 0);
816 assert(ivl_lval_part_off(lval) == 0);
817 assert(ivl_lval_width(lval) == ivl_signal_width(lsig));
819 assert((roff + use_wid) <= rvec.wid);
822 fprintf(vvp_out, " %s v%p_%lu, %u, %u;\n", command_name,
823 lsig, use_word, rvec.base+roff, use_wid);
825 if (rvec.base >= 4)
826 roff += use_wid;
830 static void force_link_rval(ivl_statement_t net, ivl_expr_t rval)
832 ivl_signal_t rsig;;
833 ivl_lval_t lval;
834 ivl_signal_t lsig;
835 const char*command_name;
837 ivl_expr_t lword_idx, rword_idx;
838 unsigned long use_lword = 0, use_rword = 0;
840 if (ivl_expr_type(rval) != IVL_EX_SIGNAL)
841 return;
843 switch (ivl_statement_type(net)) {
844 case IVL_ST_CASSIGN:
845 command_name = "%cassign";
846 break;
847 case IVL_ST_FORCE:
848 command_name = "%force";
849 break;
850 default:
851 command_name = "ERROR";
852 assert(0);
853 break;
856 rsig = ivl_expr_signal(rval);
857 assert(ivl_stmt_lvals(net) == 1);
858 lval = ivl_stmt_lval(net, 0);
859 lsig = ivl_lval_sig(lval);
861 /* At least for now, only handle force to fixed words of an array. */
862 if ((lword_idx = ivl_lval_idx(lval)) != 0) {
863 assert(number_is_immediate(lword_idx, 8*sizeof(unsigned long)));
864 use_lword = get_number_immediate(lword_idx);
867 if ((rword_idx = ivl_expr_oper1(rval)) != 0) {
868 assert(number_is_immediate(rword_idx, 8*sizeof(unsigned long)));
869 use_rword = get_number_immediate(rword_idx);
872 assert(ivl_signal_array_count(rsig) == 1);
873 use_rword = 0;
875 fprintf(vvp_out, " %s/link", command_name);
876 fprintf(vvp_out, " v%p_%lu", lsig, use_lword);
877 fprintf(vvp_out, ", v%p_%lu;\n", rsig, use_rword);
880 static int show_stmt_cassign(ivl_statement_t net)
882 ivl_expr_t rval;
883 struct vector_info rvec;
885 rval = ivl_stmt_rval(net);
886 assert(rval);
888 rvec = draw_eval_expr(rval, STUFF_OK_47);
890 /* Write out initial continuous assign instructions to assign
891 the expression value to the l-value. */
892 force_vector_to_lval(net, rvec);
894 force_link_rval(net, rval);
896 return 0;
900 * Handle the deassign similar to cassign. The lvals must all be
901 * vectors without bit or part selects. Simply call %deassign for all
902 * the values.
904 static int show_stmt_deassign(ivl_statement_t net)
906 unsigned lidx;
908 for (lidx = 0 ; lidx < ivl_stmt_lvals(net) ; lidx += 1) {
909 ivl_lval_t lval = ivl_stmt_lval(net, lidx);
910 ivl_signal_t lsig = ivl_lval_sig(lval);
912 ivl_expr_t word_idx = ivl_lval_idx(lval);
913 unsigned long use_word = 0;
915 assert(lsig != 0);
916 assert(ivl_lval_mux(lval) == 0);
917 assert(ivl_lval_part_off(lval) == 0);
919 if (word_idx != 0) {
920 assert(number_is_immediate(word_idx, 8*sizeof(use_word)));
921 use_word = get_number_immediate(word_idx);
925 fprintf(vvp_out, " %%deassign v%p_%lu;\n", lsig, use_word);
928 return 0;
931 static int show_stmt_condit(ivl_statement_t net, ivl_scope_t sscope)
933 int rc = 0;
934 unsigned lab_false, lab_out;
935 ivl_expr_t exp = ivl_stmt_cond_expr(net);
936 struct vector_info cond
937 = draw_eval_expr(exp, STUFF_OK_XZ|STUFF_OK_47|STUFF_OK_RO);
939 assert(cond.wid == 1);
941 lab_false = local_count++;
942 lab_out = local_count++;
944 fprintf(vvp_out, " %%jmp/0xz T_%d.%d, %u;\n",
945 thread_count, lab_false, cond.base);
947 /* Done with the condition expression. */
948 if (cond.base >= 8)
949 clr_vector(cond);
951 if (ivl_stmt_cond_true(net))
952 rc += show_statement(ivl_stmt_cond_true(net), sscope);
955 if (ivl_stmt_cond_false(net)) {
956 fprintf(vvp_out, " %%jmp T_%d.%d;\n", thread_count, lab_out);
957 fprintf(vvp_out, "T_%d.%u ;\n", thread_count, lab_false);
958 clear_expression_lookaside();
960 rc += show_statement(ivl_stmt_cond_false(net), sscope);
962 fprintf(vvp_out, "T_%d.%u ;\n", thread_count, lab_out);
963 clear_expression_lookaside();
965 } else {
966 fprintf(vvp_out, "T_%d.%u ;\n", thread_count, lab_false);
967 clear_expression_lookaside();
970 return rc;
974 * The delay statement is easy. Simply write a ``%delay <n>''
975 * instruction to delay the thread, then draw the included statement.
976 * The delay statement comes from verilog code like this:
978 * ...
979 * #<delay> <stmt>;
981 static int show_stmt_delay(ivl_statement_t net, ivl_scope_t sscope)
983 int rc = 0;
984 uint64_t delay = ivl_stmt_delay_val(net);
985 ivl_statement_t stmt = ivl_stmt_sub_stmt(net);
987 unsigned long low = delay % UINT64_C(0x100000000);
988 unsigned long hig = delay / UINT64_C(0x100000000);
990 fprintf(vvp_out, " %%delay %lu, %lu;\n", low, hig);
991 /* Lots of things can happen during a delay. */
992 clear_expression_lookaside();
994 rc += show_statement(stmt, sscope);
996 return rc;
1000 * The delayx statement is slightly more complex in that it is
1001 * necessary to calculate the delay first. Load the calculated delay
1002 * into and index register and use the %delayx instruction to do the
1003 * actual delay.
1005 static int show_stmt_delayx(ivl_statement_t net, ivl_scope_t sscope)
1007 int rc = 0;
1008 ivl_expr_t exp = ivl_stmt_delay_expr(net);
1009 ivl_statement_t stmt = ivl_stmt_sub_stmt(net);
1011 switch (ivl_expr_value(exp)) {
1013 case IVL_VT_BOOL:
1014 case IVL_VT_LOGIC: {
1015 struct vector_info del = draw_eval_expr(exp, 0);
1016 fprintf(vvp_out, " %%ix/get 0, %u, %u;\n",
1017 del.base, del.wid);
1018 clr_vector(del);
1019 break;
1022 case IVL_VT_REAL: {
1023 int word = draw_eval_real(exp);
1024 fprintf(vvp_out, " %%cvt/ir 0, %d;\n", word);
1025 clr_word(word);
1026 break;
1029 default:
1030 assert(0);
1033 fprintf(vvp_out, " %%delayx 0;\n");
1034 /* Lots of things can happen during a delay. */
1035 clear_expression_lookaside();
1037 rc += show_statement(stmt, sscope);
1038 return rc;
1041 static int show_stmt_disable(ivl_statement_t net, ivl_scope_t sscope)
1043 int rc = 0;
1045 ivl_scope_t target = ivl_stmt_call(net);
1046 fprintf(vvp_out, " %%disable S_%p;\n", target);
1048 return rc;
1051 static int show_stmt_force(ivl_statement_t net)
1053 ivl_expr_t rval;
1054 struct vector_info rvec;
1056 rval = ivl_stmt_rval(net);
1057 assert(rval);
1059 rvec = draw_eval_expr(rval, STUFF_OK_47);
1061 /* Write out initial continuous assign instructions to assign
1062 the expression value to the l-value. */
1063 force_vector_to_lval(net, rvec);
1065 force_link_rval(net, rval);
1067 return 0;
1070 static int show_stmt_forever(ivl_statement_t net, ivl_scope_t sscope)
1072 int rc = 0;
1073 ivl_statement_t stmt = ivl_stmt_sub_stmt(net);
1074 unsigned lab_top = local_count++;
1076 fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_top);
1077 rc += show_statement(stmt, sscope);
1078 fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_top);
1080 return rc;
1083 static int show_stmt_fork(ivl_statement_t net, ivl_scope_t sscope)
1085 unsigned idx;
1086 int rc = 0;
1087 unsigned cnt = ivl_stmt_block_count(net);
1088 ivl_scope_t scope = ivl_stmt_block_scope(net);
1090 unsigned out = transient_id++;
1091 unsigned id_base = transient_id;
1093 /* cnt is the number of sub-threads. If the fork-join has no
1094 name, then we can put one of the sub-threads in the current
1095 thread, so decrement the count by one. */
1096 if (scope == 0) {
1097 cnt -= 1;
1098 scope = sscope;
1101 transient_id += cnt;
1103 /* If no subscope use provided */
1104 if (!scope) scope = sscope;
1106 /* Draw a fork statement for all but one of the threads of the
1107 fork/join. Send the threads off to a bit of code where they
1108 are implemented. */
1109 for (idx = 0 ; idx < cnt ; idx += 1) {
1110 fprintf(vvp_out, " %%fork t_%u, S_%p;\n",
1111 id_base+idx, scope);
1114 /* If we are putting one sub-thread into the current thread,
1115 then draw its code here. */
1116 if (ivl_stmt_block_scope(net) == 0)
1117 rc += show_statement(ivl_stmt_block_stmt(net, cnt), scope);
1120 /* Generate enough joins to collect all the sub-threads. */
1121 for (idx = 0 ; idx < cnt ; idx += 1) {
1122 fprintf(vvp_out, " %%join;\n");
1124 fprintf(vvp_out, " %%jmp t_%u;\n", out);
1126 /* Generate the sub-threads themselves. */
1127 for (idx = 0 ; idx < cnt ; idx += 1) {
1128 fprintf(vvp_out, "t_%u ;\n", id_base+idx);
1129 clear_expression_lookaside();
1130 rc += show_statement(ivl_stmt_block_stmt(net, idx), scope);
1131 fprintf(vvp_out, " %%end;\n");
1134 /* This is the label for the out. Use this to branch around
1135 the implementations of all the child threads. */
1136 clear_expression_lookaside();
1137 fprintf(vvp_out, "t_%u ;\n", out);
1139 return rc;
1143 * noop statements are implemented by doing nothing.
1145 static int show_stmt_noop(ivl_statement_t net)
1147 return 0;
1150 static int show_stmt_release(ivl_statement_t net)
1152 unsigned lidx;
1154 for (lidx = 0 ; lidx < ivl_stmt_lvals(net) ; lidx += 1) {
1155 ivl_lval_t lval = ivl_stmt_lval(net, lidx);
1156 ivl_signal_t lsig = ivl_lval_sig(lval);
1157 const char*opcode = 0;
1159 ivl_expr_t word_idx = ivl_lval_idx(lval);
1160 unsigned long use_word = 0;
1161 assert(lsig != 0);
1162 assert(ivl_lval_mux(lval) == 0);
1163 assert(ivl_lval_part_off(lval) == 0);
1165 switch (ivl_signal_type(lsig)) {
1166 case IVL_SIT_REG:
1167 opcode = "reg";
1168 break;
1169 default:
1170 opcode = "net";
1171 break;
1174 if (word_idx != 0) {
1175 assert(number_is_immediate(word_idx, 8*sizeof(use_word)));
1176 use_word = get_number_immediate(word_idx);
1179 /* Generate the appropriate release statement for this
1180 l-value. */
1181 fprintf(vvp_out, " %%release/%s v%p_%lu;\n",
1182 opcode, lsig, use_word);
1185 return 0;
1188 static int show_stmt_repeat(ivl_statement_t net, ivl_scope_t sscope)
1190 int rc = 0;
1191 unsigned lab_top = local_count++, lab_out = local_count++;
1192 ivl_expr_t exp = ivl_stmt_cond_expr(net);
1193 struct vector_info cnt = draw_eval_expr(exp, 0);
1195 /* Test that 0 < expr */
1196 fprintf(vvp_out, "T_%u.%u %%cmp/u 0, %u, %u;\n", thread_count,
1197 lab_top, cnt.base, cnt.wid);
1198 clear_expression_lookaside();
1199 fprintf(vvp_out, " %%jmp/0xz T_%u.%u, 5;\n", thread_count, lab_out);
1200 /* This adds -1 (all ones in 2's complement) to the count. */
1201 fprintf(vvp_out, " %%add %u, 1, %u;\n", cnt.base, cnt.wid);
1203 rc += show_statement(ivl_stmt_sub_stmt(net), sscope);
1205 fprintf(vvp_out, " %%jmp T_%u.%u;\n", thread_count, lab_top);
1206 fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_out);
1207 clear_expression_lookaside();
1209 clr_vector(cnt);
1211 return rc;
1215 * The trigger statement is straight forward. All we have to do is
1216 * write a single bit of fake data to the event object.
1218 static int show_stmt_trigger(ivl_statement_t net)
1220 ivl_event_t ev = ivl_stmt_events(net, 0);
1221 assert(ev);
1222 fprintf(vvp_out, " %%set/v E_%p, 0,1;\n", ev);
1223 return 0;
1226 static int show_stmt_utask(ivl_statement_t net)
1228 ivl_scope_t task = ivl_stmt_call(net);
1230 fprintf(vvp_out, " %%fork TD_%s",
1231 vvp_mangle_id(ivl_scope_name(task)));
1232 fprintf(vvp_out, ", S_%p;\n", task);
1233 fprintf(vvp_out, " %%join;\n");
1234 clear_expression_lookaside();
1235 return 0;
1238 static int show_stmt_wait(ivl_statement_t net, ivl_scope_t sscope)
1240 if (ivl_stmt_nevent(net) == 1) {
1241 ivl_event_t ev = ivl_stmt_events(net, 0);
1242 fprintf(vvp_out, " %%wait E_%p;\n", ev);
1244 } else {
1245 unsigned idx;
1246 static unsigned int cascade_counter = 0;
1247 ivl_event_t ev = ivl_stmt_events(net, 0);
1248 fprintf(vvp_out, "Ewait_%u .event/or E_%p", cascade_counter, ev);
1250 for (idx = 1 ; idx < ivl_stmt_nevent(net) ; idx += 1) {
1251 ev = ivl_stmt_events(net, idx);
1252 fprintf(vvp_out, ", E_%p", ev);
1254 fprintf(vvp_out, ";\n %%wait Ewait_%u;\n", cascade_counter);
1255 cascade_counter += 1;
1257 /* Always clear the expression lookaside after a
1258 %wait. Anything can happen while the thread is waiting. */
1259 clear_expression_lookaside();
1261 return show_statement(ivl_stmt_sub_stmt(net), sscope);
1264 static struct vector_info reduction_or(struct vector_info cvec)
1266 struct vector_info result;
1268 switch (cvec.base) {
1269 case 0:
1270 result.base = 0;
1271 result.wid = 1;
1272 break;
1273 case 1:
1274 result.base = 1;
1275 result.wid = 1;
1276 break;
1277 case 2:
1278 case 3:
1279 result.base = 0;
1280 result.wid = 1;
1281 break;
1282 default:
1283 clr_vector(cvec);
1284 result.base = allocate_vector(1);
1285 result.wid = 1;
1286 fprintf(vvp_out, " %%or/r %u, %u, %u;\n", result.base,
1287 cvec.base, cvec.wid);
1288 break;
1291 return result;
1294 static int show_stmt_while(ivl_statement_t net, ivl_scope_t sscope)
1296 int rc = 0;
1297 struct vector_info cvec;
1299 unsigned top_label = local_count++;
1300 unsigned out_label = local_count++;
1302 /* Start the loop. The top of the loop starts a basic block
1303 because it can be entered from above or from the bottom of
1304 the loop. */
1305 fprintf(vvp_out, "T_%d.%d ;\n", thread_count, top_label);
1306 clear_expression_lookaside();
1308 /* Draw the evaluation of the condition expression, and test
1309 the result. If the expression evaluates to false, then
1310 branch to the out label. */
1311 cvec = draw_eval_expr(ivl_stmt_cond_expr(net), STUFF_OK_XZ|STUFF_OK_47);
1312 if (cvec.wid > 1)
1313 cvec = reduction_or(cvec);
1315 fprintf(vvp_out, " %%jmp/0xz T_%d.%d, %u;\n",
1316 thread_count, out_label, cvec.base);
1317 if (cvec.base >= 8)
1318 clr_vector(cvec);
1320 /* Draw the body of the loop. */
1321 rc += show_statement(ivl_stmt_sub_stmt(net), sscope);
1323 /* This is the bottom of the loop. branch to the top where the
1324 test is repeated, and also draw the out label. */
1325 fprintf(vvp_out, " %%jmp T_%d.%d;\n", thread_count, top_label);
1326 fprintf(vvp_out, "T_%d.%d ;\n", thread_count, out_label);
1327 clear_expression_lookaside();
1328 return rc;
1331 static int show_system_task_call(ivl_statement_t net)
1333 unsigned parm_count = ivl_stmt_parm_count(net);
1335 if (parm_count == 0) {
1336 fprintf(vvp_out, " %%vpi_call \"%s\";\n", ivl_stmt_name(net));
1337 clear_expression_lookaside();
1338 return 0;
1341 draw_vpi_task_call(net);
1343 /* VPI calls can manipulate anything, so clear the expression
1344 lookahead table after the call. */
1345 clear_expression_lookaside();
1347 return 0;
1351 * This function draws a statement as vvp assembly. It basically
1352 * switches on the statement type and draws code based on the type and
1353 * further specifics.
1355 static int show_statement(ivl_statement_t net, ivl_scope_t sscope)
1357 const ivl_statement_type_t code = ivl_statement_type(net);
1358 int rc = 0;
1360 switch (code) {
1362 case IVL_ST_ASSIGN:
1363 rc += show_stmt_assign(net);
1364 break;
1366 case IVL_ST_ASSIGN_NB:
1367 rc += show_stmt_assign_nb(net);
1368 break;
1370 case IVL_ST_BLOCK:
1371 if (ivl_stmt_block_scope(net))
1372 rc += show_stmt_block_named(net, sscope);
1373 else
1374 rc += show_stmt_block(net, sscope);
1375 break;
1377 case IVL_ST_CASE:
1378 case IVL_ST_CASEX:
1379 case IVL_ST_CASEZ:
1380 rc += show_stmt_case(net, sscope);
1381 break;
1383 case IVL_ST_CASER:
1384 rc += show_stmt_case_r(net, sscope);
1385 break;
1387 case IVL_ST_CASSIGN:
1388 rc += show_stmt_cassign(net);
1389 break;
1391 case IVL_ST_CONDIT:
1392 rc += show_stmt_condit(net, sscope);
1393 break;
1395 case IVL_ST_DEASSIGN:
1396 rc += show_stmt_deassign(net);
1397 break;
1399 case IVL_ST_DELAY:
1400 rc += show_stmt_delay(net, sscope);
1401 break;
1403 case IVL_ST_DELAYX:
1404 rc += show_stmt_delayx(net, sscope);
1405 break;
1407 case IVL_ST_DISABLE:
1408 rc += show_stmt_disable(net, sscope);
1409 break;
1411 case IVL_ST_FORCE:
1412 rc += show_stmt_force(net);
1413 break;
1415 case IVL_ST_FOREVER:
1416 rc += show_stmt_forever(net, sscope);
1417 break;
1419 case IVL_ST_FORK:
1420 rc += show_stmt_fork(net, sscope);
1421 break;
1423 case IVL_ST_NOOP:
1424 rc += show_stmt_noop(net);
1425 break;
1427 case IVL_ST_RELEASE:
1428 rc += show_stmt_release(net);
1429 break;
1431 case IVL_ST_REPEAT:
1432 rc += show_stmt_repeat(net, sscope);
1433 break;
1435 case IVL_ST_STASK:
1436 rc += show_system_task_call(net);
1437 break;
1439 case IVL_ST_TRIGGER:
1440 rc += show_stmt_trigger(net);
1441 break;
1443 case IVL_ST_UTASK:
1444 rc += show_stmt_utask(net);
1445 break;
1447 case IVL_ST_WAIT:
1448 rc += show_stmt_wait(net, sscope);
1449 break;
1451 case IVL_ST_WHILE:
1452 rc += show_stmt_while(net, sscope);
1453 break;
1455 default:
1456 fprintf(stderr, "vvp.tgt: Unable to draw statement type %u\n",
1457 code);
1458 rc += 1;
1459 break;
1462 return rc;
1467 * The process as a whole is surrounded by this code. We generate a
1468 * start label that the .thread statement can use, and we generate
1469 * code to terminate the thread.
1472 int draw_process(ivl_process_t net, void*x)
1474 int rc = 0;
1475 unsigned idx;
1476 ivl_scope_t scope = ivl_process_scope(net);
1477 ivl_statement_t stmt = ivl_process_stmt(net);
1479 int push_flag = 0;
1481 for (idx = 0 ; idx < ivl_process_attr_cnt(net) ; idx += 1) {
1483 ivl_attribute_t attr = ivl_process_attr_val(net, idx);
1485 if (strcmp(attr->key, "_ivl_schedule_push") == 0) {
1487 push_flag = 1;
1489 } else if (strcmp(attr->key, "ivl_combinational") == 0) {
1491 push_flag = 1;
1496 local_count = 0;
1497 fprintf(vvp_out, " .scope S_%p;\n", scope);
1499 /* Generate the entry label. Just give the thread a number so
1500 that we ar certain the label is unique. */
1501 fprintf(vvp_out, "T_%d ;\n", thread_count);
1502 clear_expression_lookaside();
1504 /* Draw the contents of the thread. */
1505 rc += show_statement(stmt, scope);
1508 /* Terminate the thread with either an %end instruction (initial
1509 statements) or a %jmp back to the beginning of the thread. */
1511 switch (ivl_process_type(net)) {
1513 case IVL_PR_INITIAL:
1514 fprintf(vvp_out, " %%end;\n");
1515 break;
1517 case IVL_PR_ALWAYS:
1518 fprintf(vvp_out, " %%jmp T_%d;\n", thread_count);
1519 break;
1522 /* Now write out the .thread directive that tells vvp where
1523 the thread starts. */
1525 if (push_flag) {
1526 fprintf(vvp_out, " .thread T_%d, $push;\n", thread_count);
1527 } else {
1528 fprintf(vvp_out, " .thread T_%d;\n", thread_count);
1531 thread_count += 1;
1532 return rc;
1535 int draw_task_definition(ivl_scope_t scope)
1537 int rc = 0;
1538 ivl_statement_t def = ivl_scope_def(scope);
1540 fprintf(vvp_out, "TD_%s ;\n", vvp_mangle_id(ivl_scope_name(scope)));
1541 clear_expression_lookaside();
1543 assert(def);
1544 rc += show_statement(def, scope);
1546 fprintf(vvp_out, " %%end;\n");
1548 thread_count += 1;
1549 return rc;
1552 int draw_func_definition(ivl_scope_t scope)
1554 int rc = 0;
1555 ivl_statement_t def = ivl_scope_def(scope);
1557 fprintf(vvp_out, "TD_%s ;\n", vvp_mangle_id(ivl_scope_name(scope)));
1558 clear_expression_lookaside();
1560 assert(def);
1561 rc += show_statement(def, scope);
1563 fprintf(vvp_out, " %%end;\n");
1565 thread_count += 1;
1566 return rc;