/* * Copyright (c) 2011-2025 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU * General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ # include "vvp_priv.h" # include # include # include # include /* * These functions handle the blocking assignment. Use the %set * instruction to perform the actual assignment, and calculate any * lvalues and rvalues that need calculating. * * The set_to_lvariable function takes a particular nexus and generates * the %set statements to assign the value. * * The show_stmt_assign function looks at the assign statement, scans * the l-values, and matches bits of the r-value with the correct * nexus. */ enum slice_type_e { SLICE_NO_TYPE = 0, SLICE_SIMPLE_VECTOR, SLICE_PART_SELECT_STATIC, SLICE_PART_SELECT_DYNAMIC, SLICE_MEMORY_WORD_STATIC, SLICE_MEMORY_WORD_DYNAMIC }; struct vec_slice_info { enum slice_type_e type; union { struct { unsigned long use_word; } simple_vector; struct { unsigned long part_off; } part_select_static; struct { /* Index reg that holds the memory word index */ int word_idx_reg; /* Stored x/non-x flag */ unsigned x_flag; } part_select_dynamic; struct { unsigned long use_word; } memory_word_static; struct { /* Index reg that holds the memory word index */ int word_idx_reg; /* Stored x/non-x flag */ unsigned x_flag; } memory_word_dynamic; } u_; }; static void get_vec_from_lval_slice(ivl_lval_t lval, struct vec_slice_info*slice, unsigned wid) { ivl_signal_t sig = ivl_lval_sig(lval); ivl_expr_t part_off_ex = ivl_lval_part_off(lval); unsigned long part_off = 0; /* Although Verilog doesn't support it, we'll handle here the case of an l-value part select of an array word if the address is constant. */ ivl_expr_t word_ix = ivl_lval_idx(lval); unsigned long use_word = 0; if (part_off_ex == 0) { part_off = 0; } else if (number_is_immediate(part_off_ex, IMM_WID, 0) && !number_is_unknown(part_off_ex)) { part_off = get_number_immediate(part_off_ex); part_off_ex = 0; } /* If the word index is a constant expression, then evaluate it to select the word, and pay no further heed to the expression itself. */ if (word_ix && number_is_immediate(word_ix, IMM_WID, 0)) { if (number_is_unknown(word_ix)) use_word = ULONG_MAX; // The largest valid index is ULONG_MAX - 1 else use_word = get_number_immediate(word_ix); word_ix = 0; } if (ivl_signal_dimensions(sig)==0 && part_off_ex==0 && word_ix==0 && part_off==0 && wid==ivl_signal_width(sig)) { slice->type = SLICE_SIMPLE_VECTOR; slice->u_.simple_vector.use_word = use_word; if (signal_is_return_value(sig)) { assert(use_word==0); fprintf(vvp_out, " %%retload/vec4 0;\n"); } else { fprintf(vvp_out, " %%load/vec4 v%p_%lu;\n", sig, use_word); } } else if (ivl_signal_dimensions(sig)==0 && part_off_ex==0 && word_ix==0) { assert(use_word == 0); slice->type = SLICE_PART_SELECT_STATIC; slice->u_.part_select_static.part_off = part_off; if (signal_is_return_value(sig)) { assert(use_word==0); fprintf(vvp_out, " %%retload/vec4 0;\n"); } else { fprintf(vvp_out, " %%load/vec4 v%p_%lu;\n", sig, use_word); } fprintf(vvp_out, " %%parti/u %u, %lu, 32;\n", wid, part_off); } else if (ivl_signal_dimensions(sig)==0 && part_off_ex!=0 && word_ix==0) { assert(use_word == 0); assert(part_off == 0); assert(!signal_is_return_value(sig)); // NOT IMPLEMENTED slice->type = SLICE_PART_SELECT_DYNAMIC; slice->u_.part_select_dynamic.word_idx_reg = allocate_word(); slice->u_.part_select_dynamic.x_flag = allocate_flag(); fprintf(vvp_out, " %%load/vec4 v%p_%lu;\n", sig, use_word); draw_eval_vec4(part_off_ex); fprintf(vvp_out, " %%dup/vec4;\n"); fprintf(vvp_out, " %%ix/vec4 %d;\n", slice->u_.part_select_dynamic.word_idx_reg); fprintf(vvp_out, " %%flag_mov %u, 4;\n", slice->u_.part_select_dynamic.x_flag); fprintf(vvp_out, " %%part/u %u;\n", wid); } else if (ivl_signal_dimensions(sig) > 0 && word_ix == 0) { assert(!signal_is_return_value(sig)); // NOT IMPLEMENTED slice->type = SLICE_MEMORY_WORD_STATIC; slice->u_.memory_word_static.use_word = use_word; if (use_word < ivl_signal_array_count(sig)) { fprintf(vvp_out, " %%ix/load 3, %lu, 0;\n", use_word); fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); fprintf(vvp_out, " %%load/vec4a v%p, 3;\n", sig); } else { if (wid <= 32) { fprintf(vvp_out, " %%pushi/vec4 4294967295, 4294967295, %u;\n", wid); } else { fprintf(vvp_out, " %%pushi/vec4 4294967295, 4294967295, 32;\n"); fprintf(vvp_out, " %%pad/s %u;\n", wid); } } } else if (ivl_signal_dimensions(sig) > 0 && word_ix != 0) { assert(!signal_is_return_value(sig)); // NOT IMPLEMENTED slice->type = SLICE_MEMORY_WORD_DYNAMIC; slice->u_.memory_word_dynamic.word_idx_reg = allocate_word(); slice->u_.memory_word_dynamic.x_flag = allocate_flag(); draw_eval_expr_into_integer(word_ix, slice->u_.memory_word_dynamic.word_idx_reg); fprintf(vvp_out, " %%flag_mov %u, 4;\n", slice->u_.memory_word_dynamic.x_flag); fprintf(vvp_out, " %%load/vec4a v%p, %d;\n", sig, slice->u_.memory_word_dynamic.word_idx_reg); } else { assert(0); } } /* * This loads the l-value values into the top of the stack, and also * leaves in the slices the information needed to store the slice * results back. */ static void get_vec_from_lval(ivl_statement_t net, struct vec_slice_info*slices) { unsigned lidx; unsigned cur_bit; unsigned wid = ivl_stmt_lwidth(net); cur_bit = 0; for (lidx = ivl_stmt_lvals(net) ; lidx > 0 ; lidx -= 1) { ivl_lval_t lval; unsigned bit_limit = wid - cur_bit; lval = ivl_stmt_lval(net, lidx-1); if (bit_limit > ivl_lval_width(lval)) bit_limit = ivl_lval_width(lval); get_vec_from_lval_slice(lval, slices+lidx-1, bit_limit); if (cur_bit > 0) { fprintf(vvp_out, " %%concat/vec4;\n"); } cur_bit += bit_limit; } } static void put_vec_to_ret_slice(ivl_signal_t sig, struct vec_slice_info*slice, unsigned wid) { int part_off_idx; /* If the slice of the l-value is a BOOL variable, then cast the data to a BOOL vector so that the stores can be valid. */ if (ivl_signal_data_type(sig) == IVL_VT_BOOL) { fprintf(vvp_out, " %%cast2;\n"); } switch (slice->type) { default: fprintf(vvp_out, " ; XXXX slice->type=%d\n", slice->type); assert(0); break; case SLICE_SIMPLE_VECTOR: assert(slice->u_.simple_vector.use_word == 0); fprintf(vvp_out, " %%ret/vec4 0, 0, %u;\n", wid); break; case SLICE_PART_SELECT_STATIC: part_off_idx = allocate_word(); fprintf(vvp_out, " %%ix/load %d, %lu, 0;\n", part_off_idx, slice->u_.part_select_static.part_off); fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); fprintf(vvp_out, " %%ret/vec4 0, %d, %u;\n", part_off_idx, wid); clr_word(part_off_idx); break; case SLICE_PART_SELECT_DYNAMIC: fprintf(vvp_out, " %%flag_mov 4, %u;\n", slice->u_.part_select_dynamic.x_flag); fprintf(vvp_out, " %%ret/vec4 0, %d, %u;\n", slice->u_.part_select_dynamic.word_idx_reg, wid); clr_word(slice->u_.part_select_dynamic.word_idx_reg); clr_flag(slice->u_.part_select_dynamic.x_flag); break; } } static void put_vec_to_lval_slice(ivl_lval_t lval, struct vec_slice_info*slice, unsigned wid) { //unsigned skip_set = transient_id++; ivl_signal_t sig = ivl_lval_sig(lval); int part_off_idx; /* Special Case: If the l-value signal is named after its scope, and the scope is a function, then this is an assign to a return value and should be handled differently. */ if (signal_is_return_value(sig)) { put_vec_to_ret_slice(sig, slice, wid); return; } /* If the slice of the l-value is a BOOL variable, then cast the data to a BOOL vector so that the stores can be valid. */ if (ivl_signal_data_type(sig) == IVL_VT_BOOL) { fprintf(vvp_out, " %%cast2;\n"); } switch (slice->type) { default: fprintf(vvp_out, " ; XXXX slice->type=%d\n", slice->type); assert(0); break; case SLICE_SIMPLE_VECTOR: fprintf(vvp_out, " %%store/vec4 v%p_%lu, 0, %u;\n", sig, slice->u_.simple_vector.use_word, wid); break; case SLICE_PART_SELECT_STATIC: part_off_idx = allocate_word(); fprintf(vvp_out, " %%ix/load %d, %lu, 0;\n", part_off_idx, slice->u_.part_select_static.part_off); fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); fprintf(vvp_out, " %%store/vec4 v%p_0, %d, %u;\n", sig, part_off_idx, wid); clr_word(part_off_idx); break; case SLICE_PART_SELECT_DYNAMIC: fprintf(vvp_out, " %%flag_mov 4, %u;\n", slice->u_.part_select_dynamic.x_flag); fprintf(vvp_out, " %%store/vec4 v%p_0, %d, %u;\n", sig, slice->u_.part_select_dynamic.word_idx_reg, wid); clr_word(slice->u_.part_select_dynamic.word_idx_reg); clr_flag(slice->u_.part_select_dynamic.x_flag); break; case SLICE_MEMORY_WORD_STATIC: if (slice->u_.memory_word_static.use_word < ivl_signal_array_count(sig)) { int word_idx = allocate_word(); fprintf(vvp_out," %%flag_set/imm 4, 0;\n"); fprintf(vvp_out," %%ix/load %d, %lu, 0;\n", word_idx, slice->u_.memory_word_static.use_word); fprintf(vvp_out," %%store/vec4a v%p, %d, 0;\n", sig, word_idx); clr_word(word_idx); } else { fprintf(vvp_out," ; Skip this slice write to v%p [%lu]\n", sig, slice->u_.memory_word_static.use_word); fprintf(vvp_out," %%pop/vec4 1;\n"); } break; case SLICE_MEMORY_WORD_DYNAMIC: fprintf(vvp_out, " %%flag_mov 4, %u;\n", slice->u_.memory_word_dynamic.x_flag); fprintf(vvp_out, " %%store/vec4a v%p, %d, 0;\n", sig, slice->u_.memory_word_dynamic.word_idx_reg); clr_word(slice->u_.memory_word_dynamic.word_idx_reg); clr_flag(slice->u_.memory_word_dynamic.x_flag); break; } } static void put_vec_to_lval(ivl_statement_t net, struct vec_slice_info*slices) { unsigned lidx; unsigned cur_bit; unsigned wid = ivl_stmt_lwidth(net); cur_bit = 0; for (lidx = 0 ; lidx < ivl_stmt_lvals(net) ; lidx += 1) { ivl_lval_t lval; unsigned bit_limit = wid - cur_bit; lval = ivl_stmt_lval(net, lidx); if (bit_limit > ivl_lval_width(lval)) bit_limit = ivl_lval_width(lval); if (lidx+1 < ivl_stmt_lvals(net)) fprintf(vvp_out, " %%split/vec4 %u;\n", bit_limit); put_vec_to_lval_slice(lval, slices+lidx, bit_limit); cur_bit += bit_limit; } } static ivl_type_t draw_lval_expr(ivl_lval_t lval) { ivl_lval_t lval_nest = ivl_lval_nest(lval); ivl_signal_t lval_sig = ivl_lval_sig(lval); if (lval_sig) { fprintf(vvp_out, " %%load/obj v%p_0;\n", lval_sig); return ivl_signal_net_type(lval_sig); } assert (lval_nest); ivl_type_t sub_type = draw_lval_expr(lval_nest); assert(ivl_type_base(sub_type) == IVL_VT_CLASS); if (ivl_lval_idx(lval_nest)) { fprintf(vvp_out, " ; XXXX Don't know how to handle ivl_lval_idx values here.\n"); } int prop_idx = ivl_lval_property_idx(lval_nest); fprintf(vvp_out, " %%prop/obj %d, 0; Load property %s\n", prop_idx, ivl_type_prop_name(sub_type, prop_idx)); fprintf(vvp_out, " %%pop/obj 1, 1;\n"); return ivl_type_prop_type(sub_type, prop_idx); } /* * Store a vector from the vec4 stack to the statement l-values. This * all assumes that the value to be assigned is already on the top of * the stack. * * NOTE TO SELF: The %store/vec4 takes a width, but the %assign/vec4 * instructions do not, instead relying on the expression width. I * think that it the proper way to do it, so soon I should change the * %store/vec4 to not include the width operand. */ static void store_vec4_to_lval(ivl_statement_t net) { for (unsigned lidx = 0 ; lidx < ivl_stmt_lvals(net) ; lidx += 1) { ivl_lval_t lval = ivl_stmt_lval(net,lidx); ivl_signal_t lsig = ivl_lval_sig(lval); unsigned lwid = ivl_lval_width(lval); ivl_expr_t part_off_ex = ivl_lval_part_off(lval); /* This is non-nil if the l-val is the word of a memory, and nil otherwise. */ ivl_expr_t word_ex = ivl_lval_idx(lval); if (lidx+1 < ivl_stmt_lvals(net)) fprintf(vvp_out, " %%split/vec4 %u;\n", lwid); if (word_ex) { /* Handle index into an array */ int word_index = allocate_word(); int part_index = 0; /* Calculate the word address into word_index */ draw_eval_expr_into_integer(word_ex, word_index); /* If there is a part_offset, calculate it into part_index. */ if (part_off_ex) { int flag_index = allocate_flag(); part_index = allocate_word(); fprintf(vvp_out, " %%flag_mov %d, 4;\n", flag_index); draw_eval_expr_into_integer(part_off_ex, part_index); fprintf(vvp_out, " %%flag_or 4, %d;\n", flag_index); clr_flag(flag_index); } assert(lsig); fprintf(vvp_out, " %%store/vec4a v%p, %d, %d;\n", lsig, word_index, part_index); clr_word(word_index); if (part_index) clr_word(part_index); } else if (part_off_ex) { /* Dynamically calculated part offset */ int offset_index = allocate_word(); draw_eval_expr_into_integer(part_off_ex, offset_index); /* Note that flag4 is set by the eval above. */ assert(lsig); if (signal_is_return_value(lsig)) { fprintf(vvp_out, " %%ret/vec4 0, %d, %u; Assign to %s (store_vec4_to_lval)\n", offset_index, lwid, ivl_signal_basename(lsig)); } else if (ivl_signal_type(lsig)==IVL_SIT_UWIRE) { fprintf(vvp_out, " %%force/vec4/off v%p_0, %d;\n", lsig, offset_index); } else { fprintf(vvp_out, " %%store/vec4 v%p_0, %d, %u;\n", lsig, offset_index, lwid); } clr_word(offset_index); } else { /* No offset expression, so use simpler store function. */ assert(lsig); assert(lwid == ivl_signal_width(lsig)); if (signal_is_return_value(lsig)) { fprintf(vvp_out, " %%ret/vec4 0, 0, %u; Assign to %s (store_vec4_to_lval)\n", lwid, ivl_signal_basename(lsig)); } else { fprintf(vvp_out, " %%store/vec4 v%p_0, 0, %u;\n", lsig, lwid); } } } } static unsigned int draw_array_pattern(ivl_signal_t var, ivl_expr_t rval, unsigned int array_idx) { ivl_type_t var_type = ivl_signal_net_type(var); for (unsigned int idx = 0; idx < ivl_expr_parms(rval); idx += 1) { ivl_expr_t expr = ivl_expr_parm(rval, idx); switch (ivl_expr_type(expr)) { case IVL_EX_ARRAY_PATTERN: /* Flatten nested array patterns */ array_idx = draw_array_pattern(var, expr, array_idx); break; default: switch (ivl_type_base(var_type)) { case IVL_VT_BOOL: case IVL_VT_LOGIC: draw_eval_vec4(expr); fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", array_idx); fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); fprintf(vvp_out, " %%store/vec4a v%p, 3, 0;\n", var); break; case IVL_VT_REAL: draw_eval_real(expr); fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", array_idx); fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); fprintf(vvp_out, " %%store/reala v%p, 3;\n", var); break; case IVL_VT_STRING: draw_eval_string(expr); fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", array_idx); fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); fprintf(vvp_out, " %%store/stra v%p, 3;\n", var); break; case IVL_VT_CLASS: draw_eval_object(expr); fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", array_idx); fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); fprintf(vvp_out, " %%store/obja v%p, 3;\n", var); break; default: assert(0); break; } array_idx++; break; } } return array_idx; } static void draw_stmt_assign_vector_opcode(unsigned char opcode, bool is_signed) { int idx_reg; switch (opcode) { case 0: break; case '+': fprintf(vvp_out, " %%add;\n"); break; case '-': fprintf(vvp_out, " %%sub;\n"); break; case '*': fprintf(vvp_out, " %%mul;\n"); break; case '/': fprintf(vvp_out, " %%div%s;\n", is_signed ? "/s":""); break; case '%': fprintf(vvp_out, " %%mod%s;\n", is_signed ? "/s":""); break; case '&': fprintf(vvp_out, " %%and;\n"); break; case '|': fprintf(vvp_out, " %%or;\n"); break; case '^': fprintf(vvp_out, " %%xor;\n"); break; case 'l': /* lval <<= expr */ idx_reg = allocate_word(); fprintf(vvp_out, " %%ix/vec4 %d;\n", idx_reg); fprintf(vvp_out, " %%shiftl %d;\n", idx_reg); clr_word(idx_reg); break; case 'r': /* lval >>= expr */ idx_reg = allocate_word(); fprintf(vvp_out, " %%ix/vec4 %d;\n", idx_reg); fprintf(vvp_out, " %%shiftr %d;\n", idx_reg); clr_word(idx_reg); break; case 'R': /* lval >>>= expr */ idx_reg = allocate_word(); fprintf(vvp_out, " %%ix/vec4 %d;\n", idx_reg); fprintf(vvp_out, " %%shiftr/s %d;\n", idx_reg); clr_word(idx_reg); break; default: fprintf(vvp_out, "; UNSUPPORTED ASSIGNMENT OPCODE: %c\n", opcode); assert(0); break; } } static int show_stmt_assign_vector(ivl_statement_t net) { ivl_expr_t rval = ivl_stmt_rval(net); if (ivl_expr_type(rval) == IVL_EX_ARRAY_PATTERN) { ivl_lval_t lval = ivl_stmt_lval(net, 0); ivl_signal_t sig = ivl_lval_sig(lval); draw_array_pattern(sig, rval, 0); return 0; } unsigned wid = ivl_stmt_lwidth(net); /* If this is a compressed assignment, then get the contents of the l-value. We need these values as part of the r-value calculation. */ if (ivl_stmt_opcode(net) != 0) { struct vec_slice_info *slices; slices = calloc(ivl_stmt_lvals(net), sizeof(struct vec_slice_info)); fprintf(vvp_out, " ; show_stmt_assign_vector: Get l-value for compressed %c= operand\n", ivl_stmt_opcode(net)); get_vec_from_lval(net, slices); draw_eval_vec4(rval); resize_vec4_wid(rval, wid); draw_stmt_assign_vector_opcode(ivl_stmt_opcode(net), ivl_expr_signed(rval)); put_vec_to_lval(net, slices); free(slices); } else { draw_eval_vec4(rval); resize_vec4_wid(rval, wid); store_vec4_to_lval(net); } return 0; } enum real_lval_type_e { REAL_NO_TYPE = 0, REAL_SIMPLE_WORD, REAL_MEMORY_WORD_STATIC, REAL_MEMORY_WORD_DYNAMIC }; struct real_lval_info { enum real_lval_type_e type; union { struct { unsigned long use_word; } simple_word; struct { unsigned long use_word; } memory_word_static; struct { /* Index reg that holds the memory word index */ int word_idx_reg; /* Stored x/non-x flag */ unsigned x_flag; } memory_word_dynamic; } u_; }; static void get_real_from_lval(ivl_lval_t lval, struct real_lval_info*slice) { ivl_signal_t sig = ivl_lval_sig(lval); ivl_expr_t word_ix = ivl_lval_idx(lval); unsigned long use_word = 0; /* If the word index is a constant expression, then evaluate it to select the word, and pay no further heed to the expression itself. */ if (word_ix && number_is_immediate(word_ix, IMM_WID, 0)) { if (number_is_unknown(word_ix)) use_word = ULONG_MAX; // The largest valid index is ULONG_MAX - 1 else use_word = get_number_immediate(word_ix); word_ix = 0; } if (ivl_signal_dimensions(sig)==0 && word_ix==0) { slice->type = REAL_SIMPLE_WORD; slice->u_.simple_word.use_word = use_word; if (signal_is_return_value(sig)) { assert(use_word==0); fprintf(vvp_out, " %%retload/real 0;\n"); } else { fprintf(vvp_out, " %%load/real v%p_%lu;\n", sig, use_word); } } else if (ivl_signal_dimensions(sig) > 0 && word_ix == 0) { assert(!signal_is_return_value(sig)); // NOT IMPLEMENTED slice->type = REAL_MEMORY_WORD_STATIC; slice->u_.memory_word_static.use_word = use_word; if (use_word < ivl_signal_array_count(sig)) { fprintf(vvp_out, " %%ix/load 3, %lu, 0;\n", use_word); fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); fprintf(vvp_out, " %%load/ar v%p, 3;\n", sig); } else { fprintf(vvp_out, " %%pushi/real 0, 0;\n"); } } else if (ivl_signal_dimensions(sig) > 0 && word_ix != 0) { assert(!signal_is_return_value(sig)); // NOT IMPLEMENTED slice->type = REAL_MEMORY_WORD_DYNAMIC; slice->u_.memory_word_dynamic.word_idx_reg = allocate_word(); slice->u_.memory_word_dynamic.x_flag = allocate_flag(); draw_eval_expr_into_integer(word_ix, slice->u_.memory_word_dynamic.word_idx_reg); fprintf(vvp_out, " %%flag_mov %u, 4;\n", slice->u_.memory_word_dynamic.x_flag); fprintf(vvp_out, " %%load/ar v%p, %d;\n", sig, slice->u_.memory_word_dynamic.word_idx_reg); } else { assert(0); } } static void put_real_to_lval(ivl_lval_t lval, struct real_lval_info*slice) { ivl_signal_t sig = ivl_lval_sig(lval); /* Special Case: If the l-value signal is named after its scope, and the scope is a function, then this is an assign to a return value and should be handled differently. */ if (signal_is_return_value(sig)) { assert(slice->u_.simple_word.use_word == 0); fprintf(vvp_out, " %%ret/real 0;\n"); return; } switch (slice->type) { default: fprintf(vvp_out, " ; XXXX slice->type=%d\n", slice->type); assert(0); break; case REAL_SIMPLE_WORD: fprintf(vvp_out, " %%store/real v%p_%lu;\n", sig, slice->u_.simple_word.use_word); break; case REAL_MEMORY_WORD_STATIC: if (slice->u_.memory_word_static.use_word < ivl_signal_array_count(sig)) { int word_idx = allocate_word(); fprintf(vvp_out," %%flag_set/imm 4, 0;\n"); fprintf(vvp_out," %%ix/load %d, %lu, 0;\n", word_idx, slice->u_.memory_word_static.use_word); fprintf(vvp_out," %%store/reala v%p, %d;\n", sig, word_idx); clr_word(word_idx); } else { fprintf(vvp_out," ; Skip this slice write to v%p [%lu]\n", sig, slice->u_.memory_word_static.use_word); fprintf(vvp_out," %%pop/real 1;\n"); } break; case REAL_MEMORY_WORD_DYNAMIC: fprintf(vvp_out, " %%flag_mov 4, %u;\n", slice->u_.memory_word_dynamic.x_flag); fprintf(vvp_out, " %%store/reala v%p, %d;\n", sig, slice->u_.memory_word_dynamic.word_idx_reg); clr_word(slice->u_.memory_word_dynamic.word_idx_reg); clr_flag(slice->u_.memory_word_dynamic.x_flag); break; } } static void store_real_to_lval(ivl_lval_t lval) { ivl_signal_t var; var = ivl_lval_sig(lval); assert(var != 0); /* Special Case: If the l-value signal is named after its scope, and the scope is a function, then this is an assign to a return value and should be handled differently. */ ivl_scope_t sig_scope = ivl_signal_scope(var); if ((ivl_scope_type(sig_scope) == IVL_SCT_FUNCTION) && (strcmp(ivl_signal_basename(var), ivl_scope_basename(sig_scope)) == 0)) { assert(ivl_signal_dimensions(var) == 0); fprintf(vvp_out, " %%ret/real 0; Assign to %s\n", ivl_signal_basename(var)); return; } if (ivl_signal_dimensions(var) == 0) { fprintf(vvp_out, " %%store/real v%p_0;\n", var); return; } ivl_expr_t word_ex = ivl_lval_idx(lval); int word_ix = allocate_word(); draw_eval_expr_into_integer(word_ex, word_ix); fprintf(vvp_out, " %%store/reala v%p, %d;\n", var, word_ix); clr_word(word_ix); } static void draw_stmt_assign_real_opcode(unsigned char opcode) { switch (opcode) { case 0: break; case '+': fprintf(vvp_out, " %%add/wr;\n"); break; case '-': fprintf(vvp_out, " %%sub/wr;\n"); break; case '*': fprintf(vvp_out, " %%mul/wr;\n"); break; case '/': fprintf(vvp_out, " %%div/wr;\n"); break; case '%': fprintf(vvp_out, " %%mod/wr;\n"); break; default: fprintf(vvp_out, "; UNSUPPORTED ASSIGNMENT OPCODE: %c\n", opcode); assert(0); break; } } /* * This function assigns a value to a real variable. This is destined * for /dev/null when typed ivl_signal_t takes over all the real * variable support. */ static int show_stmt_assign_sig_real(ivl_statement_t net) { ivl_lval_t lval; assert(ivl_stmt_lvals(net) == 1); lval = ivl_stmt_lval(net, 0); ivl_expr_t rval = ivl_stmt_rval(net); if (ivl_expr_type(rval) == IVL_EX_ARRAY_PATTERN) { ivl_signal_t sig = ivl_lval_sig(lval); draw_array_pattern(sig, rval, 0); return 0; } /* If this is a compressed assignment, then get the contents of the l-value. We need this value as part of the r-value calculation. */ if (ivl_stmt_opcode(net) != 0) { struct real_lval_info slice; fprintf(vvp_out, " ; show_stmt_assign_real: Get l-value for compressed %c= operand\n", ivl_stmt_opcode(net)); get_real_from_lval(lval, &slice); draw_eval_real(rval); draw_stmt_assign_real_opcode(ivl_stmt_opcode(net)); put_real_to_lval(lval, &slice); } else { draw_eval_real(rval); store_real_to_lval(lval); } return 0; } static int show_stmt_assign_sig_string(ivl_statement_t net) { ivl_lval_t lval = ivl_stmt_lval(net, 0); ivl_expr_t rval = ivl_stmt_rval(net); ivl_expr_t part = ivl_lval_part_off(lval); ivl_expr_t aidx = ivl_lval_idx(lval); ivl_signal_t var= ivl_lval_sig(lval); assert(ivl_stmt_lvals(net) == 1); assert(ivl_stmt_opcode(net) == 0); if (ivl_expr_type(rval) == IVL_EX_ARRAY_PATTERN) { draw_array_pattern(var, rval, 0); return 0; } /* Special case: If the l-value signal (string) is named after its scope, and the scope is a function, then this is an assign to a return value and should be handled differently. */ if (signal_is_return_value(var)) { assert(ivl_signal_dimensions(var) == 0); assert(part == 0 && aidx == 0); draw_eval_string(rval); fprintf(vvp_out, " %%ret/str 0; Assign to %s\n", ivl_signal_basename(var)); return 0; } /* Simplest case: no mux. Evaluate the r-value as a string and store the result into the variable. Note that the %store/str opcode pops the string result. */ if (part == 0 && aidx == 0) { draw_eval_string(rval); fprintf(vvp_out, " %%store/str v%p_0;\n", var); return 0; } /* Assign to array. The l-value has an index expression expression so we are assigning to an array word. */ if (aidx != 0) { unsigned ix; assert(part == 0); draw_eval_string(rval); draw_eval_expr_into_integer(aidx, (ix = allocate_word())); fprintf(vvp_out, " %%store/stra v%p, %u;\n", var, ix); clr_word(ix); return 0; } draw_eval_vec4(rval); resize_vec4_wid(rval, 8); /* Calculate the character select for the word. */ int mux_word = allocate_word(); draw_eval_expr_into_integer(part, mux_word); fprintf(vvp_out, " %%putc/str/vec4 v%p_0, %d;\n", var, mux_word); clr_word(mux_word); return 0; } /* * This function handles the special case that we assign an array * pattern to a dynamic array. Handle this by assigning each * element. The array pattern will have a fixed size. */ static int show_stmt_assign_darray_pattern(ivl_statement_t net) { int errors = 0; ivl_lval_t lval = ivl_stmt_lval(net, 0); ivl_expr_t rval = ivl_stmt_rval(net); ivl_signal_t var = ivl_lval_sig(lval); ivl_type_t var_type= ivl_signal_net_type(var); assert(ivl_type_base(var_type) == IVL_VT_DARRAY); ivl_type_t element_type = ivl_type_element(var_type); unsigned idx; unsigned size_reg = allocate_word(); #if 0 unsigned element_width = 1; if (ivl_type_base(element_type) == IVL_VT_BOOL) element_width = ivl_type_packed_width(element_type); else if (ivl_type_base(element_type) == IVL_VT_LOGIC) element_width = ivl_type_packed_width(element_type); #endif // FIXME: At the moment we reallocate the array space. // This probably should be a resize to avoid values glitching /* Allocate at least enough space for the array pattern. */ fprintf(vvp_out, " %%ix/load %u, %u, 0;\n", size_reg, ivl_expr_parms(rval)); /* This can not have have a X/Z value so clear flag 4. */ fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); darray_new(element_type, size_reg); fprintf(vvp_out, " %%store/obj v%p_0;\n", var); assert(ivl_expr_type(rval) == IVL_EX_ARRAY_PATTERN); for (idx = 0 ; idx < ivl_expr_parms(rval) ; idx += 1) { switch (ivl_type_base(element_type)) { case IVL_VT_BOOL: case IVL_VT_LOGIC: draw_eval_vec4(ivl_expr_parm(rval,idx)); fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx); fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); fprintf(vvp_out, " %%store/dar/vec4 v%p_0;\n", var); break; case IVL_VT_REAL: draw_eval_real(ivl_expr_parm(rval,idx)); fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx); fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); fprintf(vvp_out, " %%store/dar/r v%p_0;\n", var); break; case IVL_VT_STRING: draw_eval_string(ivl_expr_parm(rval,idx)); fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx); fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); fprintf(vvp_out, " %%store/dar/str v%p_0;\n", var); break; default: fprintf(vvp_out, "; ERROR: show_stmt_assign_darray_pattern: type_base=%d not implemented\n", ivl_type_base(element_type)); errors += 1; break; } } return errors; } /* * Loading an element and updating it is identical for queues and dynamic arrays * and is handled here. The updated value is left on the stack and will be * written back using type specific functions. */ static void show_stmt_assign_sig_darray_queue_mux(ivl_statement_t net) { ivl_lval_t lval = ivl_stmt_lval(net, 0); ivl_signal_t var = ivl_lval_sig(lval); ivl_type_t var_type = ivl_signal_net_type(var); ivl_type_t element_type = ivl_type_element(var_type); ivl_expr_t mux = ivl_lval_idx(lval); ivl_expr_t rval = ivl_stmt_rval(net); /* * Queue and dynamic array load and store functions expect the element * address in index register 3. The index expression must only be * evaluated once. So in case of an assignment operator it is moved to a * scratch register and restored to the index register once the rvalue has * been evaluated. */ switch (ivl_type_base(element_type)) { case IVL_VT_REAL: if (ivl_stmt_opcode(net) != 0) { int mux_word = allocate_word(); int flag = allocate_flag(); draw_eval_expr_into_integer(mux, 3); fprintf(vvp_out, " %%ix/mov %d, 3;\n", mux_word); fprintf(vvp_out, " %%flag_mov %d, 4;\n", flag); fprintf(vvp_out, " %%load/dar/r v%p_0;\n", var); draw_eval_real(rval); draw_stmt_assign_real_opcode(ivl_stmt_opcode(net)); fprintf(vvp_out, " %%flag_mov 4, %d;\n", flag); fprintf(vvp_out, " %%ix/mov 3, %d;\n", mux_word); clr_flag(flag); clr_word(mux_word); } else { draw_eval_real(rval); draw_eval_expr_into_integer(mux, 3); } break; case IVL_VT_STRING: assert(ivl_stmt_opcode(net) == 0); draw_eval_string(rval); draw_eval_expr_into_integer(mux, 3); break; case IVL_VT_BOOL: case IVL_VT_LOGIC: if (ivl_stmt_opcode(net) != 0) { int mux_word = allocate_word(); int flag = allocate_flag(); draw_eval_expr_into_integer(mux, 3); fprintf(vvp_out, " %%ix/mov %d, 3;\n", mux_word); fprintf(vvp_out, " %%flag_mov %d, 4;\n", flag); fprintf(vvp_out, " %%load/dar/vec4 v%p_0;\n", var); draw_eval_vec4(rval); resize_vec4_wid(rval, ivl_stmt_lwidth(net)); draw_stmt_assign_vector_opcode(ivl_stmt_opcode(net), ivl_expr_signed(rval)); fprintf(vvp_out, " %%flag_mov 4, %d;\n", flag); fprintf(vvp_out, " %%ix/mov 3, %d;\n", mux_word); clr_flag(flag); clr_word(mux_word); } else { draw_eval_vec4(rval); resize_vec4_wid(rval, ivl_stmt_lwidth(net)); draw_eval_expr_into_integer(mux, 3); } break; default: assert(0); break; } } static int show_stmt_assign_sig_darray(ivl_statement_t net) { int errors = 0; ivl_lval_t lval = ivl_stmt_lval(net, 0); ivl_expr_t rval = ivl_stmt_rval(net); ivl_expr_t part = ivl_lval_part_off(lval); ivl_signal_t var= ivl_lval_sig(lval); ivl_type_t var_type= ivl_signal_net_type(var); assert(ivl_type_base(var_type) == IVL_VT_DARRAY); ivl_type_t element_type = ivl_type_element(var_type); assert(ivl_stmt_lvals(net) == 1); assert(part == 0); if (ivl_lval_idx(lval)) { show_stmt_assign_sig_darray_queue_mux(net); switch (ivl_type_base(element_type)) { case IVL_VT_REAL: fprintf(vvp_out, " %%store/dar/r v%p_0;\n", var); break; case IVL_VT_STRING: fprintf(vvp_out, " %%store/dar/str v%p_0;\n", var); break; case IVL_VT_BOOL: case IVL_VT_LOGIC: fprintf(vvp_out, " %%store/dar/vec4 v%p_0;\n", var); break; default: assert(0); break; } } else if (ivl_expr_type(rval) == IVL_EX_ARRAY_PATTERN) { assert(ivl_stmt_opcode(net) == 0); /* There is no l-value mux, but the r-value is an array pattern. This is a special case of an assignment to elements of the l-value. */ errors += show_stmt_assign_darray_pattern(net); } else if (ivl_expr_type(rval) == IVL_EX_NEW) { assert(ivl_stmt_opcode(net) == 0); // There is no l-value mux, and the r-value expression is // a "new" expression. Handle this by simply storing the // new object to the lval. errors += draw_eval_object(rval); fprintf(vvp_out, " %%store/obj v%p_0; %s:%u: %s = new ...\n", var, ivl_stmt_file(net), ivl_stmt_lineno(net), ivl_signal_basename(var)); } else if (ivl_expr_type(rval) == IVL_EX_SIGNAL) { assert(ivl_stmt_opcode(net) == 0); // There is no l-value mux, and the r-value expression is // a "signal" expression. Store a duplicate into the lvalue // By using the %dup/obj. Remember to pop the rvalue that // is no longer needed. errors += draw_eval_object(rval); fprintf(vvp_out, " %%dup/obj;\n"); fprintf(vvp_out, " %%store/obj v%p_0; %s:%u: %s = \n", var, ivl_stmt_file(net), ivl_stmt_lineno(net), ivl_signal_basename(var)); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); } else { assert(ivl_stmt_opcode(net) == 0); // There is no l-value mux, so this must be an // assignment to the array as a whole. Evaluate the // "object", and store the evaluated result. errors += draw_eval_object(rval); fprintf(vvp_out, " %%store/obj v%p_0; %s:%u: %s = \n", var, ivl_stmt_file(net), ivl_stmt_lineno(net), ivl_signal_basename(var), ivl_expr_type(rval)); } return errors; } /* * This function handles the special case that we assign an array * pattern to a queue. Handle this by assigning each element. * The array pattern will have a fixed size. */ static int show_stmt_assign_queue_pattern(ivl_signal_t var, ivl_expr_t rval, ivl_type_t element_type, int max_idx) { int errors = 0; unsigned idx; unsigned max_size; unsigned max_elems; assert(ivl_expr_type(rval) == IVL_EX_ARRAY_PATTERN); max_size = ivl_signal_array_count(var); max_elems = ivl_expr_parms(rval); if ((max_size != 0) && (max_elems > max_size)) { fprintf(stderr, "%s:%u: Warning: Array pattern assignment has more elements " "(%u) than bounded queue '%s' supports (%u).\n" " Only using first %u elements.\n", ivl_expr_file(rval), ivl_expr_lineno(rval), max_elems, ivl_signal_basename(var), max_size, max_size); max_elems = max_size; } for (idx = 0 ; idx < max_elems ; idx += 1) { switch (ivl_type_base(element_type)) { case IVL_VT_BOOL: case IVL_VT_LOGIC: draw_eval_vec4(ivl_expr_parm(rval,idx)); fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx); fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); fprintf(vvp_out, " %%store/qdar/v v%p_0, %d, %u;\n", var, max_idx, ivl_type_packed_width(element_type)); break; case IVL_VT_REAL: draw_eval_real(ivl_expr_parm(rval,idx)); fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx); fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); fprintf(vvp_out, " %%store/qdar/r v%p_0, %d;\n", var, max_idx); break; case IVL_VT_STRING: draw_eval_string(ivl_expr_parm(rval,idx)); fprintf(vvp_out, " %%ix/load 3, %u, 0;\n", idx); fprintf(vvp_out, " %%flag_set/imm 4, 0;\n"); fprintf(vvp_out, " %%store/qdar/str v%p_0, %d;\n", var, max_idx); break; default: fprintf(vvp_out, "; ERROR: show_stmt_assign_queue_pattern: " "type_base=%d not implemented\n", ivl_type_base(element_type)); errors += 1; break; } } if ((max_size == 0) || (max_elems < max_size)) { int del_idx = allocate_word(); assert(del_idx >= 0); /* Save the first queue element to delete. */ fprintf(vvp_out, " %%ix/load %d, %u, 0;\n", del_idx, max_elems); fprintf(vvp_out, " %%delete/tail v%p_0, %d;\n", var, del_idx); clr_word(del_idx); } return errors; } static int show_stmt_assign_sig_queue(ivl_statement_t net) { int errors = 0; ivl_lval_t lval = ivl_stmt_lval(net, 0); ivl_expr_t rval = ivl_stmt_rval(net); ivl_expr_t part = ivl_lval_part_off(lval); ivl_signal_t var= ivl_lval_sig(lval); ivl_type_t var_type= ivl_signal_net_type(var); ivl_type_t element_type = ivl_type_element(var_type); assert(ivl_stmt_lvals(net) == 1); assert(part == 0); assert(ivl_type_base(var_type) == IVL_VT_QUEUE); int idx = allocate_word(); assert(idx >= 0); /* Save the queue maximum index value to an integer register. */ fprintf(vvp_out, " %%ix/load %d, %u, 0;\n", idx, ivl_signal_array_count(var)); if (ivl_expr_type(rval) == IVL_EX_NULL) { assert(ivl_stmt_opcode(net) == 0); errors += draw_eval_object(rval); fprintf(vvp_out, " %%store/obj v%p_0;\n", var); } else if (ivl_lval_idx(lval)) { show_stmt_assign_sig_darray_queue_mux(net); switch (ivl_type_base(element_type)) { case IVL_VT_REAL: fprintf(vvp_out, " %%store/qdar/r v%p_0, %d;\n", var, idx); break; case IVL_VT_STRING: fprintf(vvp_out, " %%store/qdar/str v%p_0, %d;\n", var, idx); break; case IVL_VT_BOOL: case IVL_VT_LOGIC: fprintf(vvp_out, " %%store/qdar/v v%p_0, %d, %u;\n", var, idx, ivl_type_packed_width(element_type)); break; default: assert(0); break; } } else if (ivl_expr_type(rval) == IVL_EX_ARRAY_PATTERN) { assert(ivl_stmt_opcode(net) == 0); /* There is no l-value mux, but the r-value is an array pattern. This is a special case of an assignment to the l-value. */ errors += show_stmt_assign_queue_pattern(var, rval, element_type, idx); } else { assert(ivl_stmt_opcode(net) == 0); /* There is no l-value mux, so this must be an assignment to the array as a whole. Evaluate the "object", and store the evaluated result. */ errors += draw_eval_object(rval); if (ivl_type_base(element_type) == IVL_VT_REAL) fprintf(vvp_out, " %%store/qobj/r v%p_0, %d;\n", var, idx); else if (ivl_type_base(element_type) == IVL_VT_STRING) fprintf(vvp_out, " %%store/qobj/str v%p_0, %d;\n", var, idx); else { assert(ivl_type_base(element_type) == IVL_VT_BOOL || ivl_type_base(element_type) == IVL_VT_LOGIC); fprintf(vvp_out, " %%store/qobj/v v%p_0, %d, %u;\n", var, idx, ivl_type_packed_width(element_type)); } } clr_word(idx); return errors; } static int show_stmt_assign_sig_cobject(ivl_statement_t net) { int errors = 0; ivl_lval_t lval = ivl_stmt_lval(net, 0); ivl_expr_t rval = ivl_stmt_rval(net); unsigned lwid = ivl_lval_width(lval); int prop_idx = ivl_lval_property_idx(lval); if (prop_idx >= 0) { ivl_type_t sig_type = draw_lval_expr(lval); ivl_type_t prop_type = ivl_type_prop_type(sig_type, prop_idx); if (ivl_type_base(prop_type) == IVL_VT_BOOL || ivl_type_base(prop_type) == IVL_VT_LOGIC) { assert(ivl_type_packed_dimensions(prop_type) == 0 || (ivl_type_packed_dimensions(prop_type) == 1 && ivl_type_packed_msb(prop_type,0) >= ivl_type_packed_lsb(prop_type, 0))); if (ivl_stmt_opcode(net) != 0) { fprintf(vvp_out, " %%prop/v %d;\n", prop_idx); } draw_eval_vec4(rval); if (ivl_type_base(prop_type) == IVL_VT_BOOL && ivl_expr_value(rval) != IVL_VT_BOOL) fprintf(vvp_out, " %%cast2;\n"); draw_stmt_assign_vector_opcode(ivl_stmt_opcode(net), ivl_expr_signed(rval)); fprintf(vvp_out, " %%store/prop/v %d, %u; Store in logic property %s\n", prop_idx, lwid, ivl_type_prop_name(sig_type, prop_idx)); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); } else if (ivl_type_base(prop_type) == IVL_VT_REAL) { if (ivl_stmt_opcode(net) != 0) { fprintf(vvp_out, " %%prop/r %d;\n", prop_idx); } /* Calculate the real value into the real value stack. The %store/prop/r will pop the stack value. */ draw_eval_real(rval); draw_stmt_assign_real_opcode(ivl_stmt_opcode(net)); fprintf(vvp_out, " %%store/prop/r %d;\n", prop_idx); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); } else if (ivl_type_base(prop_type) == IVL_VT_STRING) { /* Calculate the string value into the string value stack. The %store/prop/r will pop the stack value. */ draw_eval_string(rval); fprintf(vvp_out, " %%store/prop/str %d;\n", prop_idx); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); } else if (ivl_type_base(prop_type) == IVL_VT_DARRAY) { int idx = 0; /* The property is a darray, and there is no mux expression to the assignment is of an entire array object. */ errors += draw_eval_object(rval); fprintf(vvp_out, " %%store/prop/obj %d, %d; IVL_VT_DARRAY\n", prop_idx, idx); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); } else if (ivl_type_base(prop_type) == IVL_VT_CLASS) { int idx = 0; ivl_expr_t idx_expr; if ( (idx_expr = ivl_lval_idx(lval)) ) { idx = allocate_word(); } /* The property is a class object. */ errors += draw_eval_object(rval); if (idx_expr) draw_eval_expr_into_integer(idx_expr, idx); fprintf(vvp_out, " %%store/prop/obj %d, %d; IVL_VT_CLASS\n", prop_idx, idx); fprintf(vvp_out, " %%pop/obj 1, 0;\n"); if (idx_expr) clr_word(idx); } else { fprintf(vvp_out, " ; ERROR: ivl_type_base(prop_type) = %d\n", ivl_type_base(prop_type)); assert(0); } } else { ivl_signal_t sig = ivl_lval_sig(lval); assert(!ivl_lval_nest(lval)); if (ivl_expr_type(rval) == IVL_EX_ARRAY_PATTERN) { draw_array_pattern(sig, rval, 0); return 0; } /* There is no property select, so evaluate the r-value as an object and assign the entire object to the variable. */ errors += draw_eval_object(rval); if (ivl_signal_array_count(sig) > 1) { unsigned ix; ivl_expr_t aidx = ivl_lval_idx(lval); draw_eval_expr_into_integer(aidx, (ix = allocate_word())); fprintf(vvp_out, " %%store/obja v%p, %u;\n", sig, ix); clr_word(ix); } else { /* Not an array, so no index expression */ fprintf(vvp_out, " %%store/obj v%p_0;\n", sig); } } return errors; } int show_stmt_assign(ivl_statement_t net) { ivl_lval_t lval; ivl_signal_t sig; show_stmt_file_line(net, "Blocking assignment."); lval = ivl_stmt_lval(net, 0); sig = ivl_lval_sig(lval); if (sig && (ivl_signal_data_type(sig) == IVL_VT_REAL)) { return show_stmt_assign_sig_real(net); } if (sig && (ivl_signal_data_type(sig) == IVL_VT_STRING)) { return show_stmt_assign_sig_string(net); } if (sig && (ivl_signal_data_type(sig) == IVL_VT_DARRAY)) { return show_stmt_assign_sig_darray(net); } if (sig && (ivl_signal_data_type(sig) == IVL_VT_QUEUE)) { return show_stmt_assign_sig_queue(net); } if ((sig && (ivl_signal_data_type(sig) == IVL_VT_CLASS)) || ivl_lval_nest(lval)) { return show_stmt_assign_sig_cobject(net); } return show_stmt_assign_vector(net); }