1423 lines
46 KiB
C
1423 lines
46 KiB
C
/*
|
|
* Copyright (c) 2011-2020 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 <string.h>
|
|
# include <assert.h>
|
|
# include <stdlib.h>
|
|
# include <limits.h>
|
|
|
|
/*
|
|
* 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);
|
|
ivl_type_t sub_type;
|
|
|
|
if (lval_nest) {
|
|
sub_type = draw_lval_expr(lval_nest);
|
|
} else {
|
|
assert(lval_sig);
|
|
sub_type = ivl_signal_net_type(lval_sig);
|
|
assert(ivl_type_base(sub_type) == IVL_VT_CLASS);
|
|
fprintf(vvp_out, " %%load/obj v%p_0;\n", lval_sig);
|
|
}
|
|
|
|
assert(ivl_type_base(sub_type) == IVL_VT_CLASS);
|
|
if (ivl_lval_idx(lval)) {
|
|
fprintf(vvp_out, " ; XXXX Don't know how to handle ivl_lval_idx values here.\n");
|
|
}
|
|
|
|
fprintf(vvp_out, " %%prop/obj %d, 0; draw_lval_expr\n", ivl_lval_property_idx(lval));
|
|
fprintf(vvp_out, " %%pop/obj 1, 1;\n");
|
|
return ivl_type_prop_type(sub_type, ivl_lval_property_idx(lval));
|
|
}
|
|
|
|
/*
|
|
* 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);
|
|
ivl_lval_t nest = ivl_lval_nest(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 if (nest) {
|
|
/* No offset expression, but the l-value is
|
|
nested, which probably means that it is a class
|
|
member. We will use a property assign
|
|
function. */
|
|
assert(!lsig);
|
|
|
|
ivl_type_t sub_type = draw_lval_expr(nest);
|
|
assert(ivl_type_base(sub_type) == IVL_VT_CLASS);
|
|
fprintf(vvp_out, " %%store/prop/v %d, %u;\n",
|
|
ivl_lval_property_idx(lval), lwid);
|
|
fprintf(vvp_out, " %%pop/obj 1, 0;\n");
|
|
|
|
} 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 int show_stmt_assign_vector(ivl_statement_t net)
|
|
{
|
|
ivl_expr_t rval = ivl_stmt_rval(net);
|
|
//struct vector_info res;
|
|
//struct vector_info lres = {0, 0};
|
|
struct vec_slice_info*slices = 0;
|
|
int idx_reg;
|
|
|
|
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;
|
|
}
|
|
|
|
/* 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) {
|
|
fprintf(vvp_out, " ; show_stmt_assign_vector: Get l-value for compressed %c= operand\n", ivl_stmt_opcode(net));
|
|
slices = calloc(ivl_stmt_lvals(net), sizeof(struct vec_slice_info));
|
|
get_vec_from_lval(net, slices);
|
|
}
|
|
|
|
/* Handle the special case that the expression is a real
|
|
value. Evaluate the real expression, then convert the
|
|
result to a vector. Then store that vector into the
|
|
l-value. */
|
|
if (ivl_expr_value(rval) == IVL_VT_REAL) {
|
|
draw_eval_real(rval);
|
|
/* This is the accumulated with of the l-value of the
|
|
assignment. */
|
|
unsigned wid = ivl_stmt_lwidth(net);
|
|
|
|
/* Convert a calculated real value to a vec4 value of
|
|
the given width. We need to include the width of the
|
|
result because real values to not have any inherit
|
|
width. The real value will be popped, and a vec4
|
|
value pushed. */
|
|
fprintf(vvp_out, " %%cvt/vr %u;\n", wid);
|
|
|
|
} else {
|
|
unsigned wid = ivl_stmt_lwidth(net);
|
|
draw_eval_vec4(rval);
|
|
resize_vec4_wid(rval, wid);
|
|
}
|
|
|
|
switch (ivl_stmt_opcode(net)) {
|
|
case 0:
|
|
store_vec4_to_lval(net);
|
|
break;
|
|
|
|
case '+':
|
|
fprintf(vvp_out, " %%add;\n");
|
|
put_vec_to_lval(net, slices);
|
|
break;
|
|
|
|
case '-':
|
|
fprintf(vvp_out, " %%sub;\n");
|
|
put_vec_to_lval(net, slices);
|
|
break;
|
|
|
|
case '*':
|
|
fprintf(vvp_out, " %%mul;\n");
|
|
put_vec_to_lval(net, slices);
|
|
break;
|
|
|
|
case '/':
|
|
fprintf(vvp_out, " %%div%s;\n", ivl_expr_signed(rval)? "/s":"");
|
|
put_vec_to_lval(net, slices);
|
|
break;
|
|
|
|
case '%':
|
|
fprintf(vvp_out, " %%mod%s;\n", ivl_expr_signed(rval)? "/s":"");
|
|
put_vec_to_lval(net, slices);
|
|
break;
|
|
|
|
case '&':
|
|
fprintf(vvp_out, " %%and;\n");
|
|
put_vec_to_lval(net, slices);
|
|
break;
|
|
|
|
case '|':
|
|
fprintf(vvp_out, " %%or;\n");
|
|
put_vec_to_lval(net, slices);
|
|
break;
|
|
|
|
case '^':
|
|
fprintf(vvp_out, " %%xor;\n");
|
|
put_vec_to_lval(net, slices);
|
|
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);
|
|
put_vec_to_lval(net, slices);
|
|
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);
|
|
put_vec_to_lval(net, slices);
|
|
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);
|
|
put_vec_to_lval(net, slices);
|
|
break;
|
|
|
|
default:
|
|
fprintf(vvp_out, "; UNSUPPORTED ASSIGNMENT OPCODE: %c\n", ivl_stmt_opcode(net));
|
|
assert(0);
|
|
break;
|
|
}
|
|
|
|
if (slices) free(slices);
|
|
|
|
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);
|
|
}
|
|
|
|
/*
|
|
* 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)
|
|
{
|
|
struct real_lval_info*slice = 0;
|
|
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) {
|
|
fprintf(vvp_out, " ; show_stmt_assign_real: Get l-value for compressed %c= operand\n", ivl_stmt_opcode(net));
|
|
slice = calloc(1, sizeof(struct real_lval_info));
|
|
get_real_from_lval(lval, slice);
|
|
}
|
|
|
|
draw_eval_real(rval);
|
|
|
|
switch (ivl_stmt_opcode(net)) {
|
|
case 0:
|
|
store_real_to_lval(lval);
|
|
if (slice) free(slice);
|
|
return 0;
|
|
|
|
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", ivl_stmt_opcode(net));
|
|
assert(0);
|
|
break;
|
|
}
|
|
|
|
put_real_to_lval(lval, slice);
|
|
free(slice);
|
|
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;
|
|
}
|
|
|
|
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);
|
|
|
|
ivl_expr_t mux = ivl_lval_idx(lval);
|
|
|
|
assert(ivl_stmt_lvals(net) == 1);
|
|
assert(ivl_stmt_opcode(net) == 0);
|
|
assert(part == 0);
|
|
|
|
if (mux && (ivl_type_base(element_type) == IVL_VT_REAL)) {
|
|
draw_eval_real(rval);
|
|
/* The %store/dar/r expects the array index to be in index
|
|
register 3. Calculate the index in place. */
|
|
draw_eval_expr_into_integer(mux, 3);
|
|
fprintf(vvp_out, " %%store/dar/r v%p_0;\n", var);
|
|
|
|
} else if (mux && ivl_type_base(element_type) == IVL_VT_STRING) {
|
|
draw_eval_string(rval);
|
|
/* The %store/dar/str expects the array index to me in index
|
|
register 3. Calculate the index in place. */
|
|
draw_eval_expr_into_integer(mux, 3);
|
|
fprintf(vvp_out, " %%store/dar/str v%p_0;\n", var);
|
|
|
|
} else if (mux) {
|
|
draw_eval_vec4(rval);
|
|
resize_vec4_wid(rval, ivl_stmt_lwidth(net));
|
|
/* The %store/dar/vec4 expects the array index to be in index
|
|
register 3. Calculate the index in place. */
|
|
draw_eval_expr_into_integer(mux, 3);
|
|
fprintf(vvp_out, " %%store/dar/vec4 v%p_0;\n", var);
|
|
|
|
} else if (ivl_expr_type(rval) == IVL_EX_ARRAY_PATTERN) {
|
|
/* 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) {
|
|
// 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) {
|
|
|
|
// 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 = <signal>\n",
|
|
var, ivl_stmt_file(net), ivl_stmt_lineno(net),
|
|
ivl_signal_basename(var));
|
|
fprintf(vvp_out, " %%pop/obj 1, 0;\n");
|
|
|
|
} else {
|
|
// 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 = <expr type %d>\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);
|
|
|
|
ivl_expr_t mux = ivl_lval_idx(lval);
|
|
|
|
assert(ivl_stmt_lvals(net) == 1);
|
|
assert(ivl_stmt_opcode(net) == 0);
|
|
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) {
|
|
errors += draw_eval_object(rval);
|
|
fprintf(vvp_out, " %%store/obj v%p_0;\n", var);
|
|
|
|
} else if (mux && (ivl_type_base(element_type) == IVL_VT_REAL)) {
|
|
draw_eval_real(rval);
|
|
/* The %store/qdar expects the array index to be in
|
|
index register 3. */
|
|
draw_eval_expr_into_integer(mux, 3);
|
|
fprintf(vvp_out, " %%store/qdar/r v%p_0, %d;\n", var, idx);
|
|
|
|
} else if (mux && ivl_type_base(element_type) == IVL_VT_STRING) {
|
|
draw_eval_string(rval);
|
|
/* The %store/qdar expects the array index to be in
|
|
index register 3. */
|
|
draw_eval_expr_into_integer(mux, 3);
|
|
fprintf(vvp_out, " %%store/qdar/str v%p_0, %d;\n", var, idx);
|
|
|
|
} else if (mux) { // What is left must be some form of vector
|
|
assert(ivl_type_base(element_type) == IVL_VT_BOOL ||
|
|
ivl_type_base(element_type) == IVL_VT_LOGIC);
|
|
draw_eval_vec4(rval);
|
|
resize_vec4_wid(rval, ivl_stmt_lwidth(net));
|
|
/* The %store/qdar expects the array index to be in
|
|
index register 3. */
|
|
draw_eval_expr_into_integer(mux, 3);
|
|
fprintf(vvp_out, " %%store/qdar/v v%p_0, %d, %u;\n", var, idx,
|
|
ivl_type_packed_width(element_type));
|
|
|
|
} else if (ivl_expr_type(rval) == IVL_EX_ARRAY_PATTERN) {
|
|
/* 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 {
|
|
/* 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);
|
|
ivl_signal_t sig= ivl_lval_sig(lval);
|
|
unsigned lwid = ivl_lval_width(lval);
|
|
|
|
int prop_idx = ivl_lval_property_idx(lval);
|
|
|
|
if (prop_idx >= 0) {
|
|
ivl_type_t sig_type = ivl_signal_net_type(sig);
|
|
ivl_type_t prop_type = ivl_type_prop_type(sig_type, prop_idx);
|
|
|
|
if (ivl_type_base(prop_type) == IVL_VT_BOOL) {
|
|
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)));
|
|
|
|
draw_eval_vec4(rval);
|
|
if (ivl_expr_value(rval)!=IVL_VT_BOOL)
|
|
fprintf(vvp_out, " %%cast2;\n");
|
|
|
|
fprintf(vvp_out, " %%load/obj v%p_0;\n", sig);
|
|
fprintf(vvp_out, " %%store/prop/v %d, %u; Store in bool 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_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)));
|
|
|
|
draw_eval_vec4(rval);
|
|
|
|
fprintf(vvp_out, " %%load/obj v%p_0;\n", sig);
|
|
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) {
|
|
|
|
/* Calculate the real value into the real value
|
|
stack. The %store/prop/r will pop the stack
|
|
value. */
|
|
draw_eval_real(rval);
|
|
fprintf(vvp_out, " %%load/obj v%p_0;\n", sig);
|
|
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, " %%load/obj v%p_0;\n", sig);
|
|
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. */
|
|
fprintf(vvp_out, " %%load/obj v%p_0;\n", sig);
|
|
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. */
|
|
fprintf(vvp_out, " %%load/obj v%p_0;\n", sig);
|
|
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 {
|
|
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)) {
|
|
return show_stmt_assign_sig_cobject(net);
|
|
}
|
|
|
|
return show_stmt_assign_vector(net);
|
|
}
|