iverilog/tgt-vvp/eval_expr.c

3289 lines
90 KiB
C
Raw Normal View History

/*
* Copyright (c) 2001-2010 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
# include "vvp_priv.h"
# include "ivl_alloc.h"
2001-07-07 22:20:10 +02:00
# include <string.h>
2001-09-15 20:27:04 +02:00
# include <stdlib.h>
# include <assert.h>
static void draw_eval_expr_dest(ivl_expr_t expr, struct vector_info dest,
int ok_flags);
static void draw_signal_dest(ivl_expr_t expr, struct vector_info res,
int add_index, long immediate);
int number_is_unknown(ivl_expr_t expr)
2002-06-02 20:57:17 +02:00
{
const char*bits;
unsigned idx;
if (ivl_expr_type(expr) == IVL_EX_ULONG)
return 0;
assert(ivl_expr_type(expr) == IVL_EX_NUMBER);
2002-06-02 20:57:17 +02:00
bits = ivl_expr_bits(expr);
for (idx = 0 ; idx < ivl_expr_width(expr) ; idx += 1)
2002-06-02 20:57:17 +02:00
if ((bits[idx] != '0') && (bits[idx] != '1'))
return 1;
return 0;
}
/*
* This function returns TRUE if the number can be represented as a
* lim_wid immediate value. This amounts to verifying that any upper
* bits are the same. For a negative value we do not support the most
* negative twos-complement value since it can not be negated. This
* code generator always emits positive values, hence the negation
* requirement.
2002-06-02 20:57:17 +02:00
*/
int number_is_immediate(ivl_expr_t expr, unsigned lim_wid, int negative_ok_flag)
2002-06-02 20:57:17 +02:00
{
const char *bits;
unsigned nbits = ivl_expr_width(expr);
char pad_bit = '0';
2002-06-02 20:57:17 +02:00
unsigned idx;
/* We can only convert numbers to an immediate value. */
if (ivl_expr_type(expr) != IVL_EX_NUMBER
&& ivl_expr_type(expr) != IVL_EX_ULONG
&& ivl_expr_type(expr) != IVL_EX_DELAY)
return 0;
2002-06-02 20:57:17 +02:00
/* If a negative value is OK, then we really have one less
* significant bit because of the sign bit. */
if (negative_ok_flag) lim_wid -= 1;
/* This is an unsigned value so it can not have the -2**N problem. */
if (ivl_expr_type(expr) == IVL_EX_ULONG) {
unsigned long imm;
if (lim_wid >= 8*sizeof(unsigned long)) return 1;
/* At this point we know that lim_wid is smaller than an
* unsigned long variable. */
imm = ivl_expr_uvalue(expr);
if (imm < (1UL << lim_wid)) return 1;
else return 0;
}
/* This is an unsigned value so it can not have the -2**N problem. */
if (ivl_expr_type(expr) == IVL_EX_DELAY) {
uint64_t imm;
if (lim_wid >= 8*sizeof(uint64_t)) return 1;
/* At this point we know that lim_wid is smaller than a
* uint64_t variable. */
imm = ivl_expr_delay_val(expr);
if (imm < ((uint64_t)1 << lim_wid)) return 1;
else return 0;
}
bits = ivl_expr_bits(expr);
2002-06-02 20:57:17 +02:00
if (ivl_expr_signed(expr) && bits[nbits-1]=='1') pad_bit = '1';
if (pad_bit == '1' && !negative_ok_flag) return 0;
for (idx = lim_wid ; idx < nbits ; idx += 1)
if (bits[idx] != pad_bit) return 0;
/* If we have a negative number make sure it is not too big. */
if (pad_bit == '1') {
for (idx = 0; idx < lim_wid; idx += 1)
if (bits[idx] == '1') return 1;
return 0;
}
2002-06-02 20:57:17 +02:00
return 1;
}
/*
* We can return positive or negative values. You must verify that the
* number is not unknown (number_is_unknown) and is small enough
* (number_is_immediate).
*/
long get_number_immediate(ivl_expr_t expr)
2002-06-02 20:57:17 +02:00
{
long imm = 0;
2002-06-02 20:57:17 +02:00
unsigned idx;
switch (ivl_expr_type(expr)) {
2002-06-02 20:57:17 +02:00
case IVL_EX_ULONG:
imm = ivl_expr_uvalue(expr);
2002-06-02 20:57:17 +02:00
break;
case IVL_EX_NUMBER: {
const char*bits = ivl_expr_bits(expr);
unsigned nbits = ivl_expr_width(expr);
/* We can not copy more bits than fit into a long. */
if (nbits > 8*sizeof(long)) nbits = 8*sizeof(long);
2002-06-02 20:57:17 +02:00
for (idx = 0 ; idx < nbits ; idx += 1) switch (bits[idx]){
case '0':
break;
case '1':
imm |= 1L << idx;
2002-06-02 20:57:17 +02:00
break;
default:
assert(0);
}
if (ivl_expr_signed(expr) && bits[nbits-1]=='1' &&
nbits < 8*sizeof(long)) imm |= -1L << nbits;
2002-06-02 20:57:17 +02:00
break;
}
default:
assert(0);
}
return imm;
}
uint64_t get_number_immediate64(ivl_expr_t expr)
{
uint64_t imm = 0;
unsigned idx;
switch (ivl_expr_type(expr)) {
case IVL_EX_ULONG:
imm = ivl_expr_uvalue(expr);
break;
case IVL_EX_NUMBER: {
const char*bits = ivl_expr_bits(expr);
unsigned nbits = ivl_expr_width(expr);
for (idx = 0 ; idx < nbits ; idx += 1) switch (bits[idx]){
case '0':
break;
case '1':
assert(idx < 64);
imm |= UINT64_C(1) << idx;
break;
default:
assert(0);
}
if (ivl_expr_signed(expr) && bits[nbits-1]=='1' && nbits < 64)
imm |= (-UINT64_C(1)) << nbits;
break;
}
default:
assert(0);
}
return imm;
}
static void eval_logic_into_integer(ivl_expr_t expr, unsigned ix)
{
switch (ivl_expr_type(expr)) {
case IVL_EX_NUMBER:
case IVL_EX_ULONG:
{
assert(number_is_immediate(expr, IMM_WID, 1));
if (number_is_unknown(expr)) {
/* We are loading a 'bx so mimic %ix/get. */
fprintf(vvp_out, " %%ix/load %u, 0, 0;\n", ix);
fprintf(vvp_out, " %%mov 4, 1, 1;\n");
break;
}
long imm = get_number_immediate(expr);
if (imm >= 0) {
fprintf(vvp_out, " %%ix/load %u, %ld, 0;\n", ix, imm);
} else {
fprintf(vvp_out, " %%ix/load %u, 0, 0; loading %ld\n", ix, imm);
fprintf(vvp_out, " %%ix/sub %u, %ld, 0;\n", ix, -imm);
}
/* This can not have have a X/Z value so clear bit 4. */
fprintf(vvp_out, " %%mov 4, 0, 1;\n");
}
break;
case IVL_EX_SIGNAL: {
ivl_signal_t sig = ivl_expr_signal(expr);
unsigned word = 0;
if (ivl_signal_dimensions(sig) > 0) {
ivl_expr_t ixe;
const char*type = ivl_expr_signed(expr) ? "/s" : "";
/* Detect the special case that this is a
variable array. In this case, the ix/getv
will not work, so do it the hard way. */
if (ivl_signal_type(sig) == IVL_SIT_REG) {
struct vector_info rv;
rv = draw_eval_expr(expr, 0);
fprintf(vvp_out, " %%ix/get%s %u, %u, %u;\n",
type, ix, rv.base, rv.wid);
clr_vector(rv);
break;
}
ixe = ivl_expr_oper1(expr);
if (number_is_immediate(ixe, IMM_WID, 0)) {
assert(! number_is_unknown(ixe));
word = get_number_immediate(ixe);
} else {
struct vector_info rv;
rv = draw_eval_expr(expr, 0);
fprintf(vvp_out, " %%ix/get%s %u, %u, %u;\n",
type, ix, rv.base, rv.wid);
clr_vector(rv);
break;
}
}
const char*type = ivl_signal_signed(sig) ? "/s" : "";
fprintf(vvp_out, " %%ix/getv%s %u, v%p_%u;\n", type, ix,
sig, word);
break;
}
default: {
struct vector_info rv;
rv = draw_eval_expr(expr, 0);
/* Is this a signed expression? */
if (ivl_expr_signed(expr)) {
fprintf(vvp_out, " %%ix/get/s %u, %u, %u;\n",
ix, rv.base, rv.wid);
} else {
fprintf(vvp_out, " %%ix/get %u, %u, %u;\n",
ix, rv.base, rv.wid);
}
clr_vector(rv);
break;
}
}
}
/*
* This function, in addition to setting the value into index 0, sets
* bit 4 to 1 if the value is unknown.
*/
void draw_eval_expr_into_integer(ivl_expr_t expr, unsigned ix)
{
int word;
switch (ivl_expr_value(expr)) {
case IVL_VT_BOOL:
case IVL_VT_LOGIC:
eval_logic_into_integer(expr, ix);
break;
case IVL_VT_REAL:
word = draw_eval_real(expr);
fprintf(vvp_out, " %%cvt/sr %u, %u;\n", ix, word);
clr_word(word);
break;
default:
fprintf(stderr, "XXXX ivl_expr_value == %d\n",
ivl_expr_value(expr));
assert(0);
}
}
/*
* The STUFF_OK_XZ bit is true if the output is going to be further
2007-02-26 20:49:48 +01:00
* processed so that x and z values are equivalent. This may allow for
* new optimizations.
*/
static struct vector_info draw_eq_immediate(ivl_expr_t expr, unsigned ewid,
2002-06-02 20:57:17 +02:00
ivl_expr_t le,
ivl_expr_t re,
int stuff_ok_flag)
2002-06-02 20:57:17 +02:00
{
unsigned wid;
struct vector_info lv;
unsigned long imm;
assert(number_is_immediate(re, IMM_WID, 0));
assert(! number_is_unknown(re));
imm = get_number_immediate(re);
2002-06-02 20:57:17 +02:00
wid = ivl_expr_width(le);
lv = draw_eval_expr_wid(le, wid, stuff_ok_flag);
2002-06-02 20:57:17 +02:00
switch (ivl_expr_opcode(expr)) {
2002-06-02 20:57:17 +02:00
case 'E': /* === */
fprintf(vvp_out, " %%cmpi/u %u, %lu, %u;\n",
lv.base, imm, wid);
if (lv.base >= 8)
clr_vector(lv);
2002-06-02 20:57:17 +02:00
lv.base = 6;
lv.wid = 1;
break;
case 'e': /* == */
/* If this is a single bit being compared to 1, and the
output doesn't care about x vs z, then just return
the value itself. */
if ((stuff_ok_flag&STUFF_OK_XZ) && (lv.wid == 1) && (imm == 1))
break;
2002-06-02 20:57:17 +02:00
fprintf(vvp_out, " %%cmpi/u %u, %lu, %u;\n",
lv.base, imm, wid);
if (lv.base >= 8)
clr_vector(lv);
2002-06-02 20:57:17 +02:00
lv.base = 4;
lv.wid = 1;
break;
case 'N': /* !== */
fprintf(vvp_out, " %%cmpi/u %u, %lu, %u;\n",
lv.base, imm, wid);
if (lv.base >= 8)
clr_vector(lv);
2002-06-02 20:57:17 +02:00
lv.base = 6;
lv.wid = 1;
fprintf(vvp_out, " %%inv 6, 1;\n");
break;
case 'n': /* != */
/* If this is a single bit being compared to 0, and the
output doesn't care about x vs z, then just return
the value itself. */
if ((stuff_ok_flag&STUFF_OK_XZ) && (lv.wid == 1) && (imm == 0))
break;
2002-06-02 20:57:17 +02:00
fprintf(vvp_out, " %%cmpi/u %u, %lu, %u;\n",
lv.base, imm, wid);
if (lv.base >= 8)
clr_vector(lv);
2002-06-02 20:57:17 +02:00
lv.base = 4;
lv.wid = 1;
fprintf(vvp_out, " %%inv 4, 1;\n");
break;
default:
assert(0);
}
/* In the special case that 47 bits are ok, and this really is
a single bit value, then we are done. */
if ((lv.wid == 1) && (ewid == 1) && (stuff_ok_flag&STUFF_OK_47))
return lv;
2002-06-02 20:57:17 +02:00
/* Move the result out out the 4-7 bit that the compare
uses. This is because that bit may be clobbered by other
expressions. */
if (lv.base < 8) {
unsigned base = allocate_vector(ewid);
if (base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"for result of equality compare.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr), wid);
vvp_errors += 1;
}
fprintf(vvp_out, " %%mov %u, %u, 1;\n", base, lv.base);
lv.base = base;
lv.wid = ewid;
if (ewid > 1)
fprintf(vvp_out, " %%mov %u, 0, %u;\n", base+1, ewid-1);
} else if (lv.wid < ewid) {
unsigned base = allocate_vector(ewid);
if (base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"for result of equality compare.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr), wid);
vvp_errors += 1;
}
if (lv.base >= 8)
clr_vector(lv);
fprintf(vvp_out, " %%mov %u, %u, %u;\n", base,
lv.base, lv.wid);
fprintf(vvp_out, " %%mov %u, 0, %u;\n",
base+lv.wid, ewid-lv.wid);
lv.base = base;
lv.wid = ewid;
2002-06-02 20:57:17 +02:00
}
return lv;
}
/*
* This handles the special case that the operands of the comparison
* are real valued expressions.
*/
static struct vector_info draw_binary_expr_eq_real(ivl_expr_t expr)
{
struct vector_info res;
int lword, rword;
res.base = allocate_vector(1);
res.wid = 1;
assert(res.base);
lword = draw_eval_real(ivl_expr_oper1(expr));
rword = draw_eval_real(ivl_expr_oper2(expr));
clr_word(lword);
clr_word(rword);
fprintf(vvp_out, " %%cmp/wr %d, %d;\n", lword, rword);
switch (ivl_expr_opcode(expr)) {
case 'e':
fprintf(vvp_out, " %%mov %u, 4, 1;\n", res.base);
break;
case 'n': /* != */
fprintf(vvp_out, " %%mov %u, 4, 1;\n", res.base);
fprintf(vvp_out, " %%inv %u, 1;\n", res.base);
break;
default:
assert(0);
}
return res;
}
static struct vector_info draw_binary_expr_eq(ivl_expr_t expr,
unsigned ewid,
int stuff_ok_flag)
{
ivl_expr_t le = ivl_expr_oper1(expr);
ivl_expr_t re = ivl_expr_oper2(expr);
2002-06-02 20:57:17 +02:00
unsigned wid;
struct vector_info lv;
struct vector_info rv;
if ((ivl_expr_value(le) == IVL_VT_REAL)
||(ivl_expr_value(re) == IVL_VT_REAL)) {
return draw_binary_expr_eq_real(expr);
}
if (number_is_immediate(re,16,0) && !number_is_unknown(re))
return draw_eq_immediate(expr, ewid, le, re, stuff_ok_flag);
2002-06-02 20:57:17 +02:00
assert(ivl_expr_value(le) == IVL_VT_LOGIC
|| ivl_expr_value(le) == IVL_VT_BOOL);
assert(ivl_expr_value(re) == IVL_VT_LOGIC
|| ivl_expr_value(re) == IVL_VT_BOOL);
2002-06-02 20:57:17 +02:00
wid = ivl_expr_width(le);
if (ivl_expr_width(re) > wid)
wid = ivl_expr_width(re);
lv = draw_eval_expr_wid(le, wid, stuff_ok_flag&~STUFF_OK_47);
rv = draw_eval_expr_wid(re, wid, stuff_ok_flag&~STUFF_OK_47);
switch (ivl_expr_opcode(expr)) {
2001-03-27 08:43:27 +02:00
case 'E': /* === */
assert(lv.wid == rv.wid);
fprintf(vvp_out, " %%cmp/u %u, %u, %u;\n", lv.base,
rv.base, lv.wid);
if (lv.base >= 8)
clr_vector(lv);
if (rv.base >= 8)
clr_vector(rv);
2001-03-27 08:43:27 +02:00
lv.base = 6;
lv.wid = 1;
break;
case 'e': /* == */
if (lv.wid != rv.wid) {
fprintf(stderr,"vvp.tgt error: operands of == "
" have different widths: %u vs %u\n",
lv.wid, rv.wid);
}
assert(lv.wid == rv.wid);
fprintf(vvp_out, " %%cmp/u %u, %u, %u;\n", lv.base,
rv.base, lv.wid);
clr_vector(lv);
clr_vector(rv);
lv.base = 4;
lv.wid = 1;
break;
2001-03-27 08:43:27 +02:00
case 'N': /* !== */
2001-11-19 05:25:46 +01:00
if (lv.wid != rv.wid) {
fprintf(stderr,"vvp.tgt error: operands of !== "
2001-11-19 05:25:46 +01:00
" have different widths: %u vs %u\n",
lv.wid, rv.wid);
}
2001-03-27 08:43:27 +02:00
assert(lv.wid == rv.wid);
fprintf(vvp_out, " %%cmp/u %u, %u, %u;\n", lv.base,
rv.base, lv.wid);
fprintf(vvp_out, " %%inv 6, 1;\n");
clr_vector(lv);
clr_vector(rv);
lv.base = 6;
lv.wid = 1;
break;
case 'n': /* != */
2001-11-19 05:25:46 +01:00
if (lv.wid != rv.wid) {
fprintf(stderr,"vvp.tgt error: operands of != "
2001-11-19 05:25:46 +01:00
" have different widths: %u vs %u\n",
lv.wid, rv.wid);
}
assert(lv.wid == rv.wid);
fprintf(vvp_out, " %%cmp/u %u, %u, %u;\n", lv.base,
rv.base, lv.wid);
fprintf(vvp_out, " %%inv 4, 1;\n");
clr_vector(lv);
clr_vector(rv);
lv.base = 4;
lv.wid = 1;
break;
default:
assert(0);
}
if ((stuff_ok_flag&STUFF_OK_47) && (ewid == 1)) {
return lv;
}
/* Move the result out out the 4-7 bit that the compare
uses. This is because that bit may be clobbered by other
expressions. */
{ unsigned base = allocate_vector(ewid);
if (base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"for result of equality compare.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr), wid);
vvp_errors += 1;
}
fprintf(vvp_out, " %%mov %u, %u, 1;\n", base, lv.base);
lv.base = base;
2001-11-19 05:25:46 +01:00
lv.wid = ewid;
if (ewid > 1)
fprintf(vvp_out, " %%mov %u, 0, %u;\n", base+1, ewid-1);
}
return lv;
}
static struct vector_info draw_binary_expr_land(ivl_expr_t expr, unsigned wid)
2001-04-01 08:49:32 +02:00
{
ivl_expr_t le = ivl_expr_oper1(expr);
ivl_expr_t re = ivl_expr_oper2(expr);
2001-04-01 08:49:32 +02:00
struct vector_info lv;
struct vector_info rv;
lv = draw_eval_expr(le, STUFF_OK_XZ);
if ((lv.base >= 4) && (lv.wid > 1)) {
struct vector_info tmp;
clr_vector(lv);
tmp.base = allocate_vector(1);
tmp.wid = 1;
assert(tmp.base);
fprintf(vvp_out, " %%or/r %u, %u, %u;\n", tmp.base,
lv.base, lv.wid);
lv = tmp;
}
rv = draw_eval_expr(re, STUFF_OK_XZ);
if ((rv.base >= 4) && (rv.wid > 1)) {
struct vector_info tmp;
clr_vector(rv);
tmp.base = allocate_vector(1);
tmp.wid = 1;
assert(tmp.base);
fprintf(vvp_out, " %%or/r %u, %u, %u;\n", tmp.base,
rv.base, rv.wid);
rv = tmp;
}
2001-04-01 08:49:32 +02:00
if (lv.base < 4) {
if (rv.base < 4) {
unsigned lb = lv.base;
unsigned rb = rv.base;
if ((lb == 0) || (rb == 0)) {
lv.base = 0;
} else if ((lb == 1) && (rb == 1)) {
lv.base = 1;
} else {
lv.base = 2;
}
lv.wid = 1;
2001-04-01 08:49:32 +02:00
} else {
fprintf(vvp_out, " %%and %u, %u, 1;\n", rv.base, lv.base);
lv = rv;
}
} else {
fprintf(vvp_out, " %%and %u, %u, 1;\n", lv.base, rv.base);
clr_vector(rv);
}
2001-11-19 05:25:46 +01:00
/* If we only want the single bit result, then we are done. */
if (wid == 1)
return lv;
/* Write the result into a zero-padded result. */
{ unsigned base = allocate_vector(wid);
if (base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"for result of padded logical AND.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr), wid);
vvp_errors += 1;
}
2001-11-19 05:25:46 +01:00
fprintf(vvp_out, " %%mov %u, %u, 1;\n", base, lv.base);
clr_vector(lv);
lv.base = base;
lv.wid = wid;
fprintf(vvp_out, " %%mov %u, 0, %u;\n", base+1, wid-1);
}
2001-04-01 08:49:32 +02:00
return lv;
}
static struct vector_info draw_binary_expr_lor(ivl_expr_t expr, unsigned wid,
int stuff_ok_flag)
2001-04-29 22:47:39 +02:00
{
ivl_expr_t le = ivl_expr_oper1(expr);
ivl_expr_t re = ivl_expr_oper2(expr);
2001-04-29 22:47:39 +02:00
struct vector_info lv;
struct vector_info rv;
lv = draw_eval_expr(le, STUFF_OK_XZ);
2001-11-19 05:25:46 +01:00
/* if the left operand has width, then evaluate the single-bit
2007-02-26 20:49:48 +01:00
logical equivalent. */
2001-11-19 05:25:46 +01:00
if ((lv.base >= 4) && (lv.wid > 1)) {
struct vector_info tmp;
clr_vector(lv);
tmp.base = allocate_vector(1);
tmp.wid = 1;
assert(tmp.base);
2001-11-19 05:25:46 +01:00
fprintf(vvp_out, " %%or/r %u, %u, %u;\n", tmp.base,
lv.base, lv.wid);
lv = tmp;
}
/* The right expression may be left in registers 4-7 because
I'll be using it immediately. */
rv = draw_eval_expr(re, STUFF_OK_XZ|STUFF_OK_47);
2001-04-29 22:47:39 +02:00
2001-11-19 05:25:46 +01:00
/* if the right operand has width, then evaluate the single-bit
2007-02-26 20:49:48 +01:00
logical equivalent. */
2001-11-19 05:25:46 +01:00
if ((rv.base >= 4) && (rv.wid > 1)) {
struct vector_info tmp;
clr_vector(rv);
tmp.base = allocate_vector(1);
tmp.wid = 1;
assert(tmp.base);
2001-11-19 05:25:46 +01:00
fprintf(vvp_out, " %%or/r %u, %u, %u;\n", tmp.base,
rv.base, rv.wid);
rv = tmp;
}
2001-04-29 22:47:39 +02:00
if (lv.base < 4) {
if (rv.base < 4) {
unsigned lb = lv.base;
unsigned rb = rv.base;
if ((lb == 0) && (rb == 0)) {
lv.base = 0;
} else if ((lb == 1) || (rb == 1)) {
lv.base = 1;
} else {
lv.base = 2;
}
} else if (lv.base==0) {
lv = rv;
2001-04-29 22:47:39 +02:00
} else {
fprintf(vvp_out, " %%or %u, %u, 1;\n", rv.base, lv.base);
lv = rv;
}
} else if (rv.base == 0) {
; /* Just return lv. */
2001-04-29 22:47:39 +02:00
} else {
if (rv.base >= 8 && lv.base < 8 && !(stuff_ok_flag&STUFF_OK_47)) {
/* If STUFF_OK_47 is false, and rv is not in the
47 area (and lv is) then plan to or the result
into the rv instead. This can case a %mov later. */
struct vector_info tmp = lv;
lv = rv;
rv = tmp;
}
2001-04-30 07:11:18 +02:00
fprintf(vvp_out, " %%or %u, %u, 1;\n", lv.base, rv.base);
if (rv.base >= 8) clr_vector(rv);
2001-04-29 22:47:39 +02:00
}
if (wid==1 && (lv.base<4 || lv.base>=8 || (stuff_ok_flag&STUFF_OK_47)))
return lv;
2001-11-19 05:25:46 +01:00
/* If we only want the single bit result, then we are done. */
if (wid == 1) {
if (lv.base >= 4 && lv.base < 8) {
unsigned tmp = allocate_vector(1);
fprintf(vvp_out, " %%mov %u, %u, 1;\n", tmp, lv.base);
lv.base = tmp;
}
2001-11-19 05:25:46 +01:00
return lv;
}
2001-11-19 05:25:46 +01:00
/* Write the result into a zero-padded result. */
{ unsigned base = allocate_vector(wid);
if (base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"for result of padded logical OR.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr), wid);
vvp_errors += 1;
}
2001-11-19 05:25:46 +01:00
fprintf(vvp_out, " %%mov %u, %u, 1;\n", base, lv.base);
if (lv.base >= 8) clr_vector(lv);
2001-11-19 05:25:46 +01:00
lv.base = base;
lv.wid = wid;
fprintf(vvp_out, " %%mov %u, 0, %u;\n", base+1, wid-1);
}
2001-04-29 22:47:39 +02:00
return lv;
}
static struct vector_info draw_binary_expr_le_real(ivl_expr_t expr)
{
struct vector_info res;
ivl_expr_t le = ivl_expr_oper1(expr);
ivl_expr_t re = ivl_expr_oper2(expr);
int lword = draw_eval_real(le);
int rword = draw_eval_real(re);
res.base = allocate_vector(1);
res.wid = 1;
assert(res.base);
clr_word(lword);
clr_word(rword);
switch (ivl_expr_opcode(expr)) {
case '<':
fprintf(vvp_out, " %%cmp/wr %d, %d;\n", lword, rword);
fprintf(vvp_out, " %%mov %u, 5, 1;\n", res.base);
break;
case 'L': /* <= */
fprintf(vvp_out, " %%cmp/wr %d, %d;\n", lword, rword);
fprintf(vvp_out, " %%or 5, 4, 1;\n");
fprintf(vvp_out, " %%mov %u, 5, 1;\n", res.base);
break;
case '>':
fprintf(vvp_out, " %%cmp/wr %d, %d;\n", rword, lword);
fprintf(vvp_out, " %%mov %u, 5, 1;\n", res.base);
break;
case 'G': /* >= */
fprintf(vvp_out, " %%cmp/wr %d, %d;\n", rword, lword);
fprintf(vvp_out, " %%or 5, 4, 1;\n");
fprintf(vvp_out, " %%mov %u, 5, 1;\n", res.base);
break;
default:
assert(0);
}
return res;
}
static struct vector_info draw_binary_expr_le_bool(ivl_expr_t expr,
unsigned wid)
{
ivl_expr_t le = ivl_expr_oper1(expr);
ivl_expr_t re = ivl_expr_oper2(expr);
int lw, rw;
struct vector_info tmp;
char s_flag = (ivl_expr_signed(le) && ivl_expr_signed(re)) ? 's' : 'u';
assert(ivl_expr_value(le) == IVL_VT_BOOL);
assert(ivl_expr_value(re) == IVL_VT_BOOL);
lw = draw_eval_bool64(le);
rw = draw_eval_bool64(re);
switch (ivl_expr_opcode(expr)) {
case 'G':
fprintf(vvp_out, " %%cmp/w%c %u, %u;\n", s_flag, rw, lw);
fprintf(vvp_out, " %%or 5, 4, 1;\n");
break;
case 'L':
fprintf(vvp_out, " %%cmp/w%c %u, %u;\n", s_flag, lw, rw);
fprintf(vvp_out, " %%or 5, 4, 1;\n");
break;
case '<':
fprintf(vvp_out, " %%cmp/w%c %u, %u;\n", s_flag, lw, rw);
break;
case '>':
fprintf(vvp_out, " %%cmp/w%c %u, %u;\n", s_flag, rw, lw);
break;
default:
assert(0);
}
clr_word(lw);
clr_word(rw);
/* Move the result out out the 4-7 bit that the compare
uses. This is because that bit may be clobbered by other
expressions. */
{ unsigned base = allocate_vector(wid);
if (base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"for result of padded inequality compare.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr), wid);
vvp_errors += 1;
}
fprintf(vvp_out, " %%mov %u, 5, 1;\n", base);
tmp.base = base;
tmp.wid = wid;
if (wid > 1)
fprintf(vvp_out, " %%mov %u, 0, %u;\n", base+1, wid-1);
}
return tmp;
}
static struct vector_info draw_binary_expr_le(ivl_expr_t expr,
unsigned wid,
int stuff_ok_flag)
2001-04-01 09:22:42 +02:00
{
ivl_expr_t le = ivl_expr_oper1(expr);
ivl_expr_t re = ivl_expr_oper2(expr);
2001-04-01 09:22:42 +02:00
struct vector_info lv;
struct vector_info rv;
char s_flag = (ivl_expr_signed(le) && ivl_expr_signed(re)) ? 's' : 'u';
2001-04-01 09:22:42 +02:00
unsigned owid = ivl_expr_width(le);
if (ivl_expr_width(re) > owid)
owid = ivl_expr_width(re);
if (ivl_expr_value(le) == IVL_VT_REAL)
return draw_binary_expr_le_real(expr);
if (ivl_expr_value(re) == IVL_VT_REAL)
return draw_binary_expr_le_real(expr);
/* Detect the special case that we can do this with integers. */
if (ivl_expr_value(le) == IVL_VT_BOOL
&& ivl_expr_value(re) == IVL_VT_BOOL
&& owid < 64) {
return draw_binary_expr_le_bool(expr, wid);
}
assert(ivl_expr_value(le) == IVL_VT_LOGIC
|| ivl_expr_value(le) == IVL_VT_BOOL);
assert(ivl_expr_value(re) == IVL_VT_LOGIC
|| ivl_expr_value(re) == IVL_VT_BOOL);
lv.wid = 0; lv.base=0;
rv.wid = 0; rv.base=0;
2001-04-01 09:22:42 +02:00
switch (ivl_expr_opcode(expr)) {
case 'G':
rv = draw_eval_expr_wid(re, owid, STUFF_OK_XZ);
if (number_is_immediate(le,16,0) && !number_is_unknown(le)) {
2010-10-02 20:02:27 +02:00
long imm = get_number_immediate(le);
assert(imm >= 0);
2010-10-02 20:02:27 +02:00
fprintf(vvp_out, " %%cmpi/%c %u, %ld, %u;\n", s_flag,
rv.base, imm, rv.wid);
} else {
lv = draw_eval_expr_wid(le, owid, STUFF_OK_XZ);
assert(lv.wid == rv.wid);
fprintf(vvp_out, " %%cmp/%c %u, %u, %u;\n", s_flag,
rv.base, lv.base, lv.wid);
}
fprintf(vvp_out, " %%or 5, 4, 1;\n");
break;
2001-04-01 09:22:42 +02:00
case 'L':
lv = draw_eval_expr_wid(le, owid, STUFF_OK_XZ);
if (number_is_immediate(re,16,0) && !number_is_unknown(re)) {
2010-10-02 20:02:27 +02:00
long imm = get_number_immediate(re);
assert(imm >= 0);
2010-10-02 20:02:27 +02:00
fprintf(vvp_out, " %%cmpi/%c %u, %ld, %u;\n", s_flag,
lv.base, imm, lv.wid);
} else {
rv = draw_eval_expr_wid(re, owid, STUFF_OK_XZ);
assert(lv.wid == rv.wid);
fprintf(vvp_out, " %%cmp/%c %u, %u, %u;\n", s_flag,
lv.base, rv.base, lv.wid);
}
2001-04-01 09:22:42 +02:00
fprintf(vvp_out, " %%or 5, 4, 1;\n");
break;
case '<':
lv = draw_eval_expr_wid(le, owid, STUFF_OK_XZ);
if (number_is_immediate(re,16,0) && !number_is_unknown(re)) {
2010-10-02 20:02:27 +02:00
long imm = get_number_immediate(re);
assert(imm >= 0);
2010-10-02 20:02:27 +02:00
fprintf(vvp_out, " %%cmpi/%c %u, %ld, %u;\n", s_flag,
lv.base, imm, lv.wid);
} else {
rv = draw_eval_expr_wid(re, owid, STUFF_OK_XZ);
assert(lv.wid == rv.wid);
fprintf(vvp_out, " %%cmp/%c %u, %u, %u;\n", s_flag,
lv.base, rv.base, lv.wid);
}
break;
case '>':
rv = draw_eval_expr_wid(re, owid, STUFF_OK_XZ);
if (number_is_immediate(le,16,0) && !number_is_unknown(le)) {
2010-10-02 20:02:27 +02:00
long imm = get_number_immediate(le);
assert(imm >= 0);
2010-10-02 20:02:27 +02:00
fprintf(vvp_out, " %%cmpi/%c %u, %ld, %u;\n", s_flag,
rv.base, imm, rv.wid);
} else {
lv = draw_eval_expr_wid(le, owid, STUFF_OK_XZ);
assert(lv.wid == rv.wid);
fprintf(vvp_out, " %%cmp/%c %u, %u, %u;\n", s_flag,
rv.base, lv.base, lv.wid);
}
2001-04-01 09:22:42 +02:00
break;
default:
assert(0);
}
if (lv.wid > 0) clr_vector(lv);
if (rv.wid > 0) clr_vector(rv);
2001-04-01 09:22:42 +02:00
if ((stuff_ok_flag&STUFF_OK_47) && (wid == 1)) {
lv.base = 5;
lv.wid = wid;
return lv;
}
/* Move the result out out the 4-7 bit that the compare
uses. This is because that bit may be clobbered by other
expressions. */
{ unsigned base = allocate_vector(wid);
if (base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"for result of padded inequality compare.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr), wid);
vvp_errors += 1;
}
fprintf(vvp_out, " %%mov %u, 5, 1;\n", base);
lv.base = base;
2001-11-19 05:25:46 +01:00
lv.wid = wid;
if (wid > 1)
fprintf(vvp_out, " %%mov %u, 0, %u;\n", base+1, wid-1);
}
2001-04-01 09:22:42 +02:00
return lv;
}
static struct vector_info draw_logic_immediate(ivl_expr_t expr,
ivl_expr_t le,
ivl_expr_t re,
unsigned wid)
{
struct vector_info lv = draw_eval_expr_wid(le, wid, STUFF_OK_XZ);
assert(number_is_immediate(re, IMM_WID, 0));
assert(! number_is_unknown(re));
unsigned long imm = get_number_immediate(re);
assert(lv.base >= 4);
switch (ivl_expr_opcode(expr)) {
case '&':
fprintf(vvp_out, " %%andi %u, %lu, %u;\n", lv.base, imm, lv.wid);
break;
default:
assert(0);
break;
}
return lv;
}
static struct vector_info draw_binary_expr_logic(ivl_expr_t expr,
2001-04-02 05:47:49 +02:00
unsigned wid)
{
ivl_expr_t le = ivl_expr_oper1(expr);
ivl_expr_t re = ivl_expr_oper2(expr);
struct vector_info lv;
struct vector_info rv;
2001-04-02 05:47:49 +02:00
if (ivl_expr_opcode(expr) == '&') {
if (number_is_immediate(re, IMM_WID, 0) && !number_is_unknown(re))
return draw_logic_immediate(expr, le, re, wid);
if (number_is_immediate(le, IMM_WID, 0) && !number_is_unknown(le))
return draw_logic_immediate(expr, re, le, wid);
}
lv = draw_eval_expr_wid(le, wid, STUFF_OK_XZ);
rv = draw_eval_expr_wid(re, wid, STUFF_OK_XZ);
2001-04-02 05:47:49 +02:00
2001-04-15 06:07:56 +02:00
/* The result goes into the left operand, and that is returned
as the result. The instructions do not allow the lv value
to be a constant bit, so we either switch the operands, or
copy the vector into a new area. */
if (lv.base < 4) {
if (rv.base > 4) {
struct vector_info tmp = lv;
lv = rv;
rv = tmp;
} else {
struct vector_info tmp;
tmp.base = allocate_vector(lv.wid);
tmp.wid = lv.wid;
if (tmp.base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"for result of binary logic.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr),
wid);
vvp_errors += 1;
}
2001-04-15 06:07:56 +02:00
fprintf(vvp_out, " %%mov %u, %u, %u;\n",
tmp.base, lv.base, tmp.wid);
lv = tmp;
}
}
switch (ivl_expr_opcode(expr)) {
2001-04-02 05:47:49 +02:00
case '&':
fprintf(vvp_out, " %%and %u, %u, %u;\n",
2001-04-02 05:47:49 +02:00
lv.base, rv.base, wid);
break;
case '|':
fprintf(vvp_out, " %%or %u, %u, %u;\n",
2001-04-02 05:47:49 +02:00
lv.base, rv.base, wid);
break;
2001-04-15 18:37:48 +02:00
case '^':
fprintf(vvp_out, " %%xor %u, %u, %u;\n",
2001-04-15 18:37:48 +02:00
lv.base, rv.base, wid);
break;
2002-09-12 17:49:43 +02:00
case 'A': /* NAND (~&) */
fprintf(vvp_out, " %%nand %u, %u, %u;\n",
lv.base, rv.base, wid);
break;
2002-09-18 06:29:55 +02:00
case 'O': /* NOR (~|) */
fprintf(vvp_out, " %%nor %u, %u, %u;\n",
lv.base, rv.base, wid);
break;
2001-04-15 06:07:56 +02:00
case 'X': /* exclusive nor (~^) */
fprintf(vvp_out, " %%xnor %u, %u, %u;\n",
lv.base, rv.base, wid);
break;
2001-04-02 05:47:49 +02:00
default:
assert(0);
}
clr_vector(rv);
return lv;
}
/*
* Draw code to evaluate the << expression. Use the %shiftl/i0
* or %shiftr/i0 instruction to do the real work of shifting. This
* means that I can handle both left and right shifts in this
* function, with the only difference the opcode I generate at the
* end.
*/
static struct vector_info draw_binary_expr_lrs(ivl_expr_t expr, unsigned wid)
2001-04-21 03:49:17 +02:00
{
ivl_expr_t le = ivl_expr_oper1(expr);
ivl_expr_t re = ivl_expr_oper2(expr);
const char*opcode = "?";
2001-04-21 03:49:17 +02:00
struct vector_info lv;
/* Evaluate the expression that is to be shifted. */
switch (ivl_expr_opcode(expr)) {
2001-04-21 05:26:23 +02:00
case 'l': /* << (left shift) */
lv = draw_eval_expr_wid(le, wid, 0);
/* shifting 0 gets 0. */
if (lv.base == 0)
return lv;
if (lv.base < 4) {
struct vector_info tmp;
tmp.base = allocate_vector(lv.wid);
tmp.wid = lv.wid;
if (tmp.base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"for result of left shift (<<).\n",
ivl_expr_file(expr), ivl_expr_lineno(expr),
wid);
vvp_errors += 1;
}
fprintf(vvp_out, " %%mov %u, %u, %u;\n",
tmp.base, lv.base, lv.wid);
lv = tmp;
}
opcode = "%shiftl";
break;
2001-04-21 05:26:23 +02:00
case 'r': /* >> (unsigned right shift) */
/* with the right shift, there may be high bits that are
shifted into the desired width of the expression, so
we let the expression size itself, if it is bigger
then what is requested of us. */
if (wid > ivl_expr_width(le)) {
lv = draw_eval_expr_wid(le, wid, 0);
} else {
lv = draw_eval_expr_wid(le, ivl_expr_width(le), 0);
}
/* shifting 0 gets 0. */
if (lv.base == 0)
return lv;
if (lv.base < 4) {
struct vector_info tmp;
tmp.base = allocate_vector(lv.wid);
tmp.wid = lv.wid;
if (tmp.base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"for result of right shift (>>).\n",
ivl_expr_file(expr), ivl_expr_lineno(expr),
lv.wid);
vvp_errors += 1;
}
fprintf(vvp_out, " %%mov %u, %u, %u;\n",
tmp.base, lv.base, lv.wid);
lv = tmp;
}
opcode = "%shiftr";
2001-04-21 05:26:23 +02:00
break;
2003-06-18 05:55:18 +02:00
case 'R': /* >>> (signed right shift) */
/* with the right shift, there may be high bits that are
shifted into the desired width of the expression, so
we let the expression size itself, if it is bigger
then what is requested of us. */
if (wid > ivl_expr_width(le)) {
lv = draw_eval_expr_wid(le, wid, 0);
} else {
lv = draw_eval_expr_wid(le, ivl_expr_width(le), 0);
}
/* shifting 0 gets 0. */
if (lv.base == 0)
return lv;
2003-06-18 05:55:18 +02:00
/* Sign extend any constant begets itself, if this
expression is signed. */
if ((lv.base < 4) && (ivl_expr_signed(expr)))
return lv;
2003-06-18 05:55:18 +02:00
if (lv.base < 4) {
struct vector_info tmp;
tmp.base = allocate_vector(lv.wid);
tmp.wid = lv.wid;
if (tmp.base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"for result of right shift (>>>).\n",
ivl_expr_file(expr), ivl_expr_lineno(expr),
lv.wid);
vvp_errors += 1;
}
2003-06-18 05:55:18 +02:00
fprintf(vvp_out, " %%mov %u, %u, %u;\n",
tmp.base, lv.base, lv.wid);
lv = tmp;
}
if (ivl_expr_signed(expr))
2003-06-18 05:55:18 +02:00
opcode = "%shiftr/s";
else
opcode = "%shiftr";
break;
2001-04-21 05:26:23 +02:00
default:
assert(0);
}
/* Figure out the shift amount and load that into the index
register. The value may be a constant, or may need to be
evaluated at run time. */
eval_logic_into_integer(re,0);
fprintf(vvp_out, " %s/i0 %u, %u;\n", opcode, lv.base, lv.wid);
if (lv.base >= 8)
save_expression_lookaside(lv.base, expr, lv.wid);
return lv;
2001-04-21 05:26:23 +02:00
}
static struct vector_info draw_load_add_immediate(ivl_expr_t le,
ivl_expr_t re,
unsigned wid,
int signed_flag)
{
struct vector_info lv;
long imm;
assert(! number_is_unknown(re));
assert(number_is_immediate(re, IMM_WID, 1));
imm = get_number_immediate(re);
lv.base = allocate_vector(wid);
lv.wid = wid;
if (lv.base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"for result of addition.\n",
ivl_expr_file(le), ivl_expr_lineno(le), wid);
vvp_errors += 1;
}
/* Load the signal value with a %load that adds the index
register to the value being loaded. */
draw_signal_dest(le, lv, signed_flag, imm);
return lv;
}
static struct vector_info draw_add_immediate(ivl_expr_t le,
ivl_expr_t re,
unsigned wid)
{
struct vector_info lv;
unsigned long imm;
lv = draw_eval_expr_wid(le, wid, STUFF_OK_XZ);
assert(lv.wid == wid);
assert(! number_is_unknown(re));
assert(number_is_immediate(re, IMM_WID, 0));
imm = get_number_immediate(re);
/* This shouldn't generally happen, because the elaborator
should take care of simple constant propagation like this,
but it doesn't have to and it is easy to catch here. */
if (imm == 0)
return lv;
switch (lv.base) {
case 0: /* Left expression is 0. */
lv.base = allocate_vector(wid);
if (lv.base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"for result of addition.\n",
ivl_expr_file(re), ivl_expr_lineno(re), wid);
vvp_errors += 1;
}
fprintf(vvp_out, " %%movi %u, %lu %u;\n", lv.base, imm, wid);
break;
case 1: /* Left expression is 1...1 (i.e. -1) */
imm -= 1;
if (imm == 0) {
lv.base = 0;
} else {
lv.base = allocate_vector(wid);
if (lv.base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"for result of addition.\n",
ivl_expr_file(re), ivl_expr_lineno(re), wid);
vvp_errors += 1;
}
fprintf(vvp_out, " %%movi %u, %lu %u;\n", lv.base, imm, wid);
}
break;
case 2: /* Left expression is 'bx or 'bz */
case 3:
lv.base = 2;
break;
default: /* The regular case. */
fprintf(vvp_out, " %%addi %u, %lu, %u;\n", lv.base, imm, wid);
break;
2002-05-30 03:57:23 +02:00
}
return lv;
}
/*
* The subi is restricted to imm operands that are <= 16 bits wide.
*/
static struct vector_info draw_sub_immediate(ivl_expr_t le,
ivl_expr_t re,
unsigned wid)
{
struct vector_info lv;
unsigned long imm;
2003-08-03 05:53:38 +02:00
unsigned tmp;
lv = draw_eval_expr_wid(le, wid, STUFF_OK_XZ);
assert(lv.wid == wid);
assert(! number_is_unknown(re));
assert(number_is_immediate(re, IMM_WID, 0));
imm = get_number_immediate(re);
if (imm == 0)
return lv;
2003-08-03 05:53:38 +02:00
switch (lv.base) {
case 0:
case 1:
tmp = allocate_vector(wid);
if (tmp == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"for result of subtraction.\n",
ivl_expr_file(le), ivl_expr_lineno(le), wid);
vvp_errors += 1;
}
fprintf(vvp_out, " %%mov %u, %u, %u;\n", tmp, lv.base, wid);
2003-08-03 05:53:38 +02:00
lv.base = tmp;
fprintf(vvp_out, " %%subi %u, %lu, %u;\n", lv.base, imm, wid);
break;
2003-08-03 05:53:38 +02:00
case 2:
case 3:
lv.base = 2;
break;
2003-08-03 05:53:38 +02:00
default:
fprintf(vvp_out, " %%subi %u, %lu, %u;\n", lv.base, imm, wid);
break;
2003-08-03 05:53:38 +02:00
}
return lv;
}
static struct vector_info draw_mul_immediate(ivl_expr_t le,
ivl_expr_t re,
unsigned wid)
{
struct vector_info lv;
unsigned long imm;
lv = draw_eval_expr_wid(le, wid, STUFF_OK_XZ);
assert(lv.wid == wid);
assert(! number_is_unknown(re));
assert(number_is_immediate(re, IMM_WID, 0));
imm = get_number_immediate(re);
if (imm == 0)
return lv;
fprintf(vvp_out, " %%muli %u, %lu, %u;\n", lv.base, imm, lv.wid);
return lv;
}
static struct vector_info draw_binary_expr_arith(ivl_expr_t expr, unsigned wid)
{
ivl_expr_t le = ivl_expr_oper1(expr);
ivl_expr_t re = ivl_expr_oper2(expr);
struct vector_info lv;
struct vector_info rv;
int signed_flag = ivl_expr_signed(le) && ivl_expr_signed(re) ? 1 : 0;
const char*sign_string = signed_flag ? "/s" : "";
2002-04-14 20:41:34 +02:00
if ((ivl_expr_opcode(expr) == '+')
&& (ivl_expr_type(le) == IVL_EX_SIGNAL)
&& (ivl_expr_type(re) == IVL_EX_ULONG)
&& number_is_immediate(re, IMM_WID, 1))
return draw_load_add_immediate(le, re, wid, signed_flag);
if ((ivl_expr_opcode(expr) == '+')
&& (ivl_expr_type(le) == IVL_EX_SIGNAL)
&& (ivl_expr_type(re) == IVL_EX_NUMBER)
&& (! number_is_unknown(re))
&& number_is_immediate(re, IMM_WID, 1))
return draw_load_add_immediate(le, re, wid, signed_flag);
if ((ivl_expr_opcode(expr) == '+')
&& (ivl_expr_type(re) == IVL_EX_SIGNAL)
&& (ivl_expr_type(le) == IVL_EX_ULONG)
&& number_is_immediate(re, IMM_WID, 1))
return draw_load_add_immediate(re, le, wid, signed_flag);
if ((ivl_expr_opcode(expr) == '+')
&& (ivl_expr_type(re) == IVL_EX_SIGNAL)
&& (ivl_expr_type(le) == IVL_EX_NUMBER)
&& (! number_is_unknown(le))
&& number_is_immediate(le, IMM_WID, 1))
return draw_load_add_immediate(re, le, wid, signed_flag);
if ((ivl_expr_opcode(expr) == '+')
&& (ivl_expr_type(re) == IVL_EX_ULONG)
&& number_is_immediate(re, IMM_WID, 0))
return draw_add_immediate(le, re, wid);
if ((ivl_expr_opcode(expr) == '+')
2002-05-30 03:57:23 +02:00
&& (ivl_expr_type(re) == IVL_EX_NUMBER)
&& (! number_is_unknown(re))
&& number_is_immediate(re, IMM_WID, 0))
return draw_add_immediate(le, re, wid);
if ((ivl_expr_opcode(expr) == '-')
&& (ivl_expr_type(re) == IVL_EX_ULONG)
&& number_is_immediate(re, IMM_WID, 0))
return draw_sub_immediate(le, re, wid);
if ((ivl_expr_opcode(expr) == '-')
&& (ivl_expr_type(re) == IVL_EX_NUMBER)
&& (! number_is_unknown(re))
&& number_is_immediate(re, IMM_WID, 0))
return draw_sub_immediate(le, re, wid);
if ((ivl_expr_opcode(expr) == '*')
&& (ivl_expr_type(re) == IVL_EX_NUMBER)
&& (! number_is_unknown(re))
&& number_is_immediate(re, IMM_WID, 0))
return draw_mul_immediate(le, re, wid);
lv = draw_eval_expr_wid(le, wid, STUFF_OK_XZ);
rv = draw_eval_expr_wid(re, wid, STUFF_OK_XZ|STUFF_OK_RO);
if (lv.wid != wid) {
fprintf(stderr, "XXXX ivl_expr_opcode(expr) = %c,"
" lv.wid=%u, wid=%u\n", ivl_expr_opcode(expr),
lv.wid, wid);
}
assert(lv.wid == wid);
assert(rv.wid == wid);
/* The arithmetic instructions replace the left operand with
the result. If the left operand is a replicated constant,
2007-02-26 20:49:48 +01:00
then I need to make a writable copy so that the
instruction can operate. */
if (lv.base < 4) {
struct vector_info tmp;
tmp.base = allocate_vector(wid);
tmp.wid = wid;
if (tmp.base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"for result of arithmetic expression.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr), wid);
vvp_errors += 1;
}
fprintf(vvp_out, " %%mov %u, %u, %u;\n", tmp.base,
lv.base, wid);
lv = tmp;
}
switch (ivl_expr_opcode(expr)) {
2001-05-24 06:20:10 +02:00
case '+':
2001-05-02 03:57:25 +02:00
fprintf(vvp_out, " %%add %u, %u, %u;\n", lv.base, rv.base, wid);
2001-05-24 06:20:10 +02:00
break;
case '-':
fprintf(vvp_out, " %%sub %u, %u, %u;\n", lv.base, rv.base, wid);
break;
case '*':
fprintf(vvp_out, " %%mul %u, %u, %u;\n", lv.base, rv.base, wid);
break;
case '/':
2002-04-14 20:41:34 +02:00
fprintf(vvp_out, " %%div%s %u, %u, %u;\n", sign_string,
lv.base, rv.base, wid);
break;
2001-05-24 06:20:10 +02:00
case '%':
fprintf(vvp_out, " %%mod%s %u, %u, %u;\n", sign_string,
lv.base, rv.base, wid);
2001-05-24 06:20:10 +02:00
break;
case 'p':
if (ivl_expr_signed(le) || ivl_expr_signed(re)) {
2010-07-24 04:24:10 +02:00
fprintf(vvp_out, " %%pow/s %u, %u, %u;\n",
lv.base, rv.base, wid);
} else {
2010-07-24 04:24:10 +02:00
fprintf(vvp_out, " %%pow %u, %u, %u;\n",
lv.base, rv.base, wid);
}
break;
2001-05-24 06:20:10 +02:00
default:
assert(0);
}
clr_vector(rv);
return lv;
}
static struct vector_info draw_binary_expr(ivl_expr_t expr,
unsigned wid,
int stuff_ok_flag)
{
struct vector_info rv;
int stuff_ok_used_flag = 0;
switch (ivl_expr_opcode(expr)) {
2001-04-01 08:49:32 +02:00
case 'a': /* && (logical and) */
rv = draw_binary_expr_land(expr, wid);
2001-04-01 08:49:32 +02:00
break;
2001-03-27 08:43:27 +02:00
case 'E': /* === */
case 'e': /* == */
2001-03-27 08:43:27 +02:00
case 'N': /* !== */
case 'n': /* != */
rv = draw_binary_expr_eq(expr, wid, stuff_ok_flag);
stuff_ok_used_flag = 1;
break;
2001-04-01 09:22:42 +02:00
case '<':
case '>':
2001-04-01 09:22:42 +02:00
case 'L': /* <= */
case 'G': /* >= */
rv = draw_binary_expr_le(expr, wid, stuff_ok_flag);
stuff_ok_used_flag = 1;
2001-04-01 09:22:42 +02:00
break;
case '+':
2001-05-02 03:57:25 +02:00
case '-':
case '*':
case '/':
2001-05-24 06:20:10 +02:00
case '%':
case 'p':
rv = draw_binary_expr_arith(expr, wid);
break;
2001-04-21 05:26:23 +02:00
case 'l': /* << */
case 'r': /* >> */
2003-06-18 05:55:18 +02:00
case 'R': /* >>> */
rv = draw_binary_expr_lrs(expr, wid);
2001-04-21 03:49:17 +02:00
break;
2001-04-29 22:47:39 +02:00
case 'o': /* || (logical or) */
rv = draw_binary_expr_lor(expr, wid, stuff_ok_flag);
stuff_ok_used_flag = 1;
2001-04-29 22:47:39 +02:00
break;
2001-04-02 05:47:49 +02:00
case '&':
case '|':
2001-04-15 18:37:48 +02:00
case '^':
2002-09-12 17:49:43 +02:00
case 'A': /* NAND (~&) */
2002-09-18 06:29:55 +02:00
case 'O': /* NOR (~|) */
2002-09-12 17:49:43 +02:00
case 'X': /* XNOR (~^) */
rv = draw_binary_expr_logic(expr, wid);
2001-04-02 05:47:49 +02:00
break;
default:
fprintf(stderr, "vvp.tgt error: unsupported binary (%c)\n",
ivl_expr_opcode(expr));
assert(0);
}
/* Mark in the lookaside that this value is done. If any OK
flags besides the STUFF_OK_47 flag are set, then the result
may not be a pure one, so clear the lookaside for the range
instead of setting in to the new expression result.
The stuff_ok_used_flag tells me if the stuff_ok_flag was
even used by anything. If not, then I can ignore it in the
following logic. */
if (rv.base >= 8) {
if (stuff_ok_used_flag && (stuff_ok_flag & ~STUFF_OK_47))
save_expression_lookaside(rv.base, 0, wid);
else
save_expression_lookaside(rv.base, expr, wid);
}
return rv;
}
/*
* The concatenation operator is evaluated by evaluating each sub-
2007-02-26 20:49:48 +01:00
* expression, then copying it into the contiguous vector of the
* result. Do this until the result vector is filled.
*/
static struct vector_info draw_concat_expr(ivl_expr_t expr, unsigned wid,
int stuff_ok_flag)
{
unsigned rep, expr_wid, concat_wid, num_sube, idx;
struct vector_info res;
int alloc_exclusive = (stuff_ok_flag&STUFF_OK_RO) ? 0 : 1;
/* Find out how wide the base concatenation expression is. */
num_sube = ivl_expr_parms(expr);
expr_wid = 0;
for (idx = 0 ; idx < num_sube; idx += 1) {
expr_wid += ivl_expr_width(ivl_expr_parm(expr, idx));
}
/* Get the repeat count. This must be a constant that has been
evaluated at compile time. The operands will be repeated to
form the result. */
rep = ivl_expr_repeat(expr);
/* Allocate a vector to hold the result. */
if (rep == 0) {
/* If the replication is zero we need to allocate temporary
* space to build the concatenation. */
res.base = allocate_vector(expr_wid);
res.wid = expr_wid;
} else {
res.base = allocate_vector(wid);
res.wid = wid;
}
if (res.base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"for result of concatenation.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr),
rep ? wid : expr_wid);
vvp_errors += 1;
}
/* If the result is the right size we can just build this in place. */
concat_wid = rep*expr_wid;
if (concat_wid <= wid) {
unsigned off = 0;
/* Evaluate the base expression. */
for (idx = num_sube; idx > 0; idx -= 1) {
ivl_expr_t arg = ivl_expr_parm(expr, idx-1);
unsigned awid = ivl_expr_width(arg);
2002-09-27 18:33:34 +02:00
struct vector_info avec;
assert(awid+off <= expr_wid);
2002-09-27 18:33:34 +02:00
/* Try to locate the subexpression in the
* lookaside map and use it when available. */
avec.base = allocate_vector_exp(arg, awid, alloc_exclusive);
2002-09-27 18:33:34 +02:00
avec.wid = awid;
if (avec.base != 0) {
assert(awid == avec.wid);
fprintf(vvp_out, " %%mov %u, %u, %u; Reuse "
"calculated expression.\n",
res.base+off, avec.base, awid);
2010-07-24 04:24:10 +02:00
clr_vector(avec);
} else {
struct vector_info dest;
dest.base = res.base+off;
dest.wid = awid;
draw_eval_expr_dest(arg, dest, 0);
}
off += awid;
}
/* Now repeat the expression as needed. */
if (rep != 0) {
rep -= 1;
} else {
/* Clear the temporary space and return nothing.
* This will be caught in draw_eval_expr_dest()
* and dropped. */
2010-07-24 04:24:10 +02:00
clr_vector(res);
res.base = 0;
res.wid = 0;
}
while (rep > 0) {
fprintf(vvp_out, " %%mov %u, %u, %u; Repetition %u\n",
res.base+expr_wid*rep, res.base, expr_wid,
rep+1);
rep -= 1;
}
/* Pad the expression when needed. */
if (wid > concat_wid) {
/* We can get a signed concatenation with $signed({...}). */
if (ivl_expr_signed(expr)) {
unsigned base = res.base+concat_wid-1;
for (idx = 1; idx <= wid-concat_wid; idx += 1) {
fprintf(vvp_out, " %%mov %u, %u, 1;\n",
base+idx, base);
}
} else {
fprintf(vvp_out, " %%mov %u, 0, %u;\n",
res.base+concat_wid, wid-concat_wid);
}
}
} else {
/* The concatenation is too big for the result so draw it
* at full width and then copy the bits that are needed. */
struct vector_info full_res;
full_res = draw_concat_expr(expr, concat_wid, stuff_ok_flag);
assert(full_res.base);
fprintf(vvp_out, " %%mov %u, %u, %u;\n", res.base,
full_res.base, wid);
2010-07-24 04:24:10 +02:00
clr_vector(full_res);
}
2002-09-27 18:33:34 +02:00
/* Save the accumulated result in the lookaside map. */
if (res.base >= 8)
save_expression_lookaside(res.base, expr, wid);
2002-09-27 18:33:34 +02:00
return res;
}
/*
* A number in an expression is made up by copying constant bits into
* the allocated vector.
*/
static struct vector_info draw_number_expr(ivl_expr_t expr, unsigned wid)
{
unsigned idx;
2001-03-29 07:16:25 +02:00
unsigned nwid;
struct vector_info res;
const char*bits = ivl_expr_bits(expr);
unsigned long val;
unsigned val_bits;
unsigned val_addr;
res.wid = wid;
2001-03-29 07:16:25 +02:00
nwid = wid;
if (ivl_expr_width(expr) < nwid)
nwid = ivl_expr_width(expr);
/* If all the bits of the number have the same value, then we
can use a constant bit. There is no need to allocate wr
bits, and there is no need to generate any code. */
2001-03-29 07:16:25 +02:00
for (idx = 1 ; idx < nwid ; idx += 1) {
if (bits[idx] != bits[0])
break;
}
2001-03-29 07:16:25 +02:00
if (idx >= res.wid) {
switch (bits[0]) {
case '0':
res.base = 0;
break;
case '1':
res.base = 1;
break;
case 'x':
res.base = 2;
break;
case 'z':
res.base = 3;
break;
default:
assert(0);
res.base = 0;
}
return res;
}
/* The number value needs to be represented as an allocated
vector. Allocate the vector and use %mov instructions to
load the constant bit values. */
res.base = allocate_vector(wid);
if (res.base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"for number value.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr), wid);
vvp_errors += 1;
}
/* Detect the special case that the entire number fits in an
immediate. In this case we generate a single %movi
instruction. */
if ((!number_is_unknown(expr)) && number_is_immediate(expr, IMM_WID,0)) {
unsigned long val2 = get_number_immediate(expr);
fprintf(vvp_out, " %%movi %u, %lu, %u;\n", res.base, val2, wid);
return res;
}
/* Use %movi as much as possible to build the value into the
destination. Use the %mov to handle the remaining general
bits. */
idx = 0;
val = 0;
val_bits = 0;
val_addr = res.base;
2001-03-29 07:16:25 +02:00
while (idx < nwid) {
char src = 0;
switch (bits[idx]) {
case '0':
val_bits += 1;
break;
case '1':
val |= 1UL << val_bits;
val_bits += 1;
break;
case 'x':
src = '2';
break;
case 'z':
src = '3';
break;
}
if (val_bits >= IMM_WID
|| (val_bits>0 && src != 0)
|| (val_bits>0 && idx+1==nwid)) {
fprintf(vvp_out, " %%movi %u, %lu, %u;\n",
val_addr, val, val_bits);
val_addr += val_bits;
val_bits = 0;
val = 0;
}
if (src != 0) {
unsigned cnt;
assert(val_bits == 0);
for (cnt = 1 ; idx+cnt < nwid ; cnt += 1)
if (bits[idx+cnt] != bits[idx])
break;
fprintf(vvp_out, " %%mov %u, %c, %u;\n", val_addr, src, cnt);
val_addr += cnt;
idx += cnt-1;
}
idx += 1;
}
2001-03-29 07:16:25 +02:00
/* Pad the number up to the expression width. */
2003-06-11 04:23:45 +02:00
if (idx < wid) {
if (ivl_expr_signed(expr) && bits[nwid-1] == '1')
2003-06-11 04:23:45 +02:00
fprintf(vvp_out, " %%mov %u, 1, %u;\n",
res.base+idx, wid-idx);
else if (bits[nwid-1] == 'x')
fprintf(vvp_out, " %%mov %u, 2, %u;\n",
res.base+idx, wid-idx);
else if (bits[nwid-1] == 'z')
fprintf(vvp_out, " %%mov %u, 3, %u;\n",
res.base+idx, wid-idx);
else
fprintf(vvp_out, " %%mov %u, 0, %u;\n",
res.base+idx, wid-idx);
}
2001-03-29 07:16:25 +02:00
2002-09-27 18:33:34 +02:00
if (res.base >= 8)
save_expression_lookaside(res.base, expr, wid);
2002-09-27 18:33:34 +02:00
return res;
}
/*
* This little helper function generates the instructions to pad a
* vector in place. It is assumed that the calling function has set up
2010-10-02 20:02:27 +02:00
* the first sub_width bits of the dest vector, and the signed_flag is
* true if the extension is to be signed.
*/
static void pad_in_place(struct vector_info dest, unsigned sub_width, int signed_flag)
{
if (signed_flag) {
unsigned idx;
for (idx = sub_width ; idx < dest.wid ; idx += 1)
fprintf(vvp_out, " %%mov %u, %u, 1;\n",
dest.base+idx, dest.base+sub_width-1);
} else {
fprintf(vvp_out, " %%mov %u, 0, %u;\n",
dest.base+sub_width, dest.wid - sub_width);
}
}
/*
* The PAD expression takes a smaller node and pads it out to a larger
* value. It will zero extend or sign extend depending on the
* signedness of the expression.
*/
static struct vector_info draw_pad_expr(ivl_expr_t expr, unsigned wid)
{
struct vector_info subv;
struct vector_info res;
ivl_expr_t subexpr = ivl_expr_oper1(expr);
/* If the sub-expression is at least as wide as the target
width, then instead of pad, we truncate. Evaluate the
expression and return that expression with the width
reduced to what we want. */
if (wid <= ivl_expr_width(subexpr)) {
subv = draw_eval_expr(subexpr, 0);
assert(subv.wid >= wid);
res.base = subv.base;
res.wid = wid;
if (subv.base >= 8)
save_expression_lookaside(subv.base, expr, subv.wid);
return res;
}
/* So now we know that the subexpression is smaller then the
desired result (the usual case) so we build the
result. Evaluate the subexpression into the target buffer,
then pad it as appropriate. */
res.base = allocate_vector(wid);
if (res.base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"to pad expression.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr), wid);
vvp_errors += 1;
}
res.wid = wid;
subv.base = res.base;
subv.wid = ivl_expr_width(subexpr);
draw_eval_expr_dest(subexpr, subv, 0);
pad_in_place(res, subv.wid, ivl_expr_signed(expr));
save_expression_lookaside(res.base, expr, wid);
return res;
}
static struct vector_info draw_realnum_expr(ivl_expr_t expr, unsigned wid)
{
struct vector_info res;
double val = ivl_expr_dvalue(expr);
long ival = val;
assert(wid <= 8*sizeof(long));
unsigned addr, run, idx;
int bit;
fprintf(vvp_out, "; draw_realnum_expr(%f, %u) as %ld\n",
val, wid, ival);
res.base = allocate_vector(wid);
res.wid = wid;
assert(res.base);
addr = res.base;
run = 1;
bit = ival & 1;
ival >>= 1;
for (idx = 1 ; idx < wid ; idx += 1) {
int next_bit = ival & 1;
ival >>= 1;
if (next_bit == bit) {
run += 1;
continue;
}
fprintf(vvp_out, " %%mov %u, %d, %u;\n", addr, bit, run);
addr += run;
run = 1;
bit = next_bit;
}
fprintf(vvp_out, " %%mov %u, %d, %u;\n", addr, bit, run);
return res;
}
static char *process_octal_codes(const char *in, unsigned width)
{
unsigned idx = 0;
unsigned ridx = 0;
unsigned str_len = strlen(in);
char *out = (char *)malloc(str_len+1);
/* If we do not have any octal codes just return the input. */
if (width/8 == str_len) {
strcpy(out, in);
return out;
}
while (ridx < str_len) {
/* An octal constant always has three digits. */
if (in[ridx] == '\\') {
out[idx] = (in[ridx+1]-'0')*64 + (in[ridx+2]-'0')*8 +
(in[ridx+3]-'0');
idx += 1;
ridx += 4;
} else {
out[idx] = in[ridx];
idx += 1;
ridx += 1;
}
}
out[idx] = '\0';
return out;
}
/*
* A string in an expression is made up by copying constant bits into
* the allocated vector.
*/
static struct vector_info draw_string_expr(ivl_expr_t expr, unsigned wid)
{
struct vector_info res;
char *p, *fp;
unsigned ewid, nwid;
unsigned idx;
res.wid = wid;
nwid = wid;
ewid = ivl_expr_width(expr);
if (ewid < nwid)
nwid = ewid;
/* Our string may have embedded \xxx sequences so they need
to be removed before we proceed. Returns a new string. */
fp = process_octal_codes(ivl_expr_string(expr), ewid);
p = fp;
p += (ewid / 8) - 1;
/* The string needs to be represented as an allocated
vector. Allocate the vector and use %mov instructions to
load the constant bit values. */
res.base = allocate_vector(wid);
if (res.base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"for string value.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr), wid);
vvp_errors += 1;
}
/* Since this is a string, we know that all the bits are
defined and each character represents exactly 8 bits. Use
the %movi instruction to more efficiently move the string
around. */
idx = 0;
while (idx < nwid) {
unsigned bits;
unsigned trans = IMM_WID;
if (nwid-idx < trans)
trans = nwid-idx;
bits = *p;
p -= 1;
if (trans > 8) {
bits |= *p << 8;
p -= 1;
if (trans > 16) {
bits |= *p << 16;
p -= 1;
if (trans > 24) {
bits |= *p << 24;
p -= 1;
}
}
}
fprintf(vvp_out, " %%movi %u, %u, %u;\n", res.base+idx,bits,trans);
idx += trans;
}
/* Pad the number up to the expression width. */
if (idx < wid)
fprintf(vvp_out, " %%mov %u, 0, %u;\n", res.base+idx, wid-idx);
2002-09-27 18:33:34 +02:00
if (res.base >= 8)
save_expression_lookaside(res.base, expr, wid);
2002-09-27 18:33:34 +02:00
free(fp);
return res;
}
/*
* This function is given an expression, the preallocated vector
* result, and the swid that is filled in so far. The caller has
* already calculated the load swid bits of exp into the beginning of
* the res vector. This function just calculates the pad to fill out
* the res area.
*/
void pad_expr_in_place(ivl_expr_t expr, struct vector_info res, unsigned swid)
{
if (res.wid <= swid)
return;
if (ivl_expr_signed(expr)) {
unsigned idx;
for (idx = swid ; idx < res.wid ; idx += 1)
fprintf(vvp_out, " %%mov %u, %u, 1;\n",
res.base+idx, res.base+swid-1);
} else {
unsigned base = res.base+swid;
unsigned count = res.wid-swid;
/* The %movi is faster for larger widths, but for very
small counts, the %mov is faster. */
if (count > 4)
fprintf(vvp_out, " %%movi %u, 0, %u;\n", base, count);
else
fprintf(vvp_out, " %%mov %u, 0, %u;\n", base, count);
}
}
/*
* Evaluating a signal expression means loading the bits of the signal
* into the thread bits. Remember to account for the part select by
* offsetting the read from the lsi (least significant index) of the
* signal.
*
* If the add_index is 0, then generate a %load/vp0 to add the
* word0 value to the loaded value before storing it into the
* destination. If the add_index is 1, then generate a %load/vp0/s to
* do a signed load.
*/
static void draw_signal_dest(ivl_expr_t expr, struct vector_info res,
int add_index, long immediate)
{
unsigned swid = ivl_expr_width(expr);
ivl_signal_t sig = ivl_expr_signal(expr);
unsigned word = 0;
if (swid > res.wid)
swid = res.wid;
/* If this is an access to an array, handle that by emitting a
load/av instruction. */
if (ivl_signal_dimensions(sig) > 0) {
ivl_expr_t ix = ivl_expr_oper1(expr);
draw_eval_expr_into_integer(ix, 3);
if (add_index < 0) {
fprintf(vvp_out, " %%load/av %u, v%p, %u;\n",
res.base, sig, swid);
pad_expr_in_place(expr, res, swid);
} else {
const char*sign_flag = (add_index>0)? "/s" : "";
/* Add an immediate value to an array value. */
fprintf(vvp_out, " %%ix/load 0, %lu, 0;\n", immediate);
fprintf(vvp_out, " %%load/avp0%s %u, v%p, %u;\n",
sign_flag, res.base, sig, res.wid);
}
return;
}
if (ivl_signal_data_type(sig) == IVL_VT_REAL) {
int tmp;
assert(add_index < 0);
tmp = allocate_word();
fprintf(vvp_out, " %%load/wr %d, v%p_%u;\n", tmp, sig, word);
fprintf(vvp_out, " %%cvt/vr %u, %d, %u;\n", res.base, tmp, res.wid);
clr_word(tmp);
} else if (add_index >= 0) {
const char*sign_flag = add_index==1? "/s" : "";
/* If this is a REG (a variable) then I can do a vector read. */
if (immediate >= 0) {
fprintf(vvp_out, " %%ix/load 0, %lu, 0;\n", immediate);
} else {
fprintf(vvp_out, " %%ix/load 0, 0, 0; immediate=%ld\n", immediate);
fprintf(vvp_out, " %%ix/sub 0, %ld, 0;\n", -immediate);
}
fprintf(vvp_out, " %%load/vp0%s %u, v%p_%u, %u;\n", sign_flag,
res.base, sig,word, res.wid);
swid = res.wid;
} else {
/* If this is a REG (a variable) then I can do a vector read. */
fprintf(vvp_out, " %%load/v %u, v%p_%u, %u;\n",
res.base, sig, word, swid);
}
pad_expr_in_place(expr, res, swid);
}
static struct vector_info draw_signal_expr(ivl_expr_t expr, unsigned wid,
int stuff_ok_flag)
{
struct vector_info res;
int alloc_exclusive = (stuff_ok_flag&STUFF_OK_RO) ? 0 : 1;
/* Already in the vector lookaside? */
res.base = allocate_vector_exp(expr, wid, alloc_exclusive);
res.wid = wid;
if (res.base != 0) {
fprintf(vvp_out, "; Reuse signal base=%u wid=%u from lookaside.\n",
res.base, res.wid);
return res;
}
res.base = allocate_vector(wid);
res.wid = wid;
if (res.base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"to load variable/wire.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr), wid);
vvp_errors += 1;
}
save_expression_lookaside(res.base, expr, wid);
draw_signal_dest(expr, res, -1, 0L);
return res;
}
2007-04-14 06:43:01 +02:00
static struct vector_info draw_select_array(ivl_expr_t sube,
ivl_expr_t bit_idx,
unsigned bit_width,
unsigned wid)
{
unsigned idx, label;
2007-04-14 06:43:01 +02:00
ivl_signal_t sig = ivl_expr_signal(sube);
/* unsigned sig_wid = ivl_expr_width(sube); */
2007-04-14 06:43:01 +02:00
ivl_expr_t ix = ivl_expr_oper1(sube);
struct vector_info shiv;
struct vector_info res;
shiv = draw_eval_expr(bit_idx, STUFF_OK_XZ|STUFF_OK_RO);
draw_eval_expr_into_integer(ix, 3);
label = local_count++;
/* We can safely skip the bit index load below if the array word
* index is undefined. We need to do this so that the bit index
* load does not reset bit 4 to zero by loading a defined value. */
fprintf(vvp_out, " %%jmp/1 T_%d.%d, 4;\n", thread_count, label);
if (ivl_expr_signed(bit_idx)) {
fprintf(vvp_out, " %%ix/get/s 0, %u, %u;\n", shiv.base,
shiv.wid);
} else {
fprintf(vvp_out, " %%ix/get 0, %u, %u;\n", shiv.base, shiv.wid);
}
fprintf(vvp_out, "T_%d.%d ;\n", thread_count, label);
2007-04-14 06:43:01 +02:00
if (shiv.base >= 8)
clr_vector(shiv);
res.base = allocate_vector(wid);
res.wid = wid;
if (res.base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"to hold selected value.\n",
ivl_expr_file(sube), ivl_expr_lineno(sube), wid);
vvp_errors += 1;
}
2007-04-14 06:43:01 +02:00
for (idx = 0 ; idx < wid ; idx += 1) {
fprintf(vvp_out, " %%load/avx.p %u, v%p, 0;\n", res.base+idx, sig);
2007-04-14 06:43:01 +02:00
}
return res;
}
static struct vector_info draw_select_signal(ivl_expr_t expr,
ivl_expr_t sube,
ivl_expr_t bit_idx,
2005-12-22 16:42:22 +01:00
unsigned bit_wid,
unsigned wid)
{
ivl_signal_t sig = ivl_expr_signal(sube);
struct vector_info res;
/* Use this word of the signal. */
unsigned use_word = 0;
unsigned use_wid, lab_x, lab_end;
2007-04-14 06:43:01 +02:00
/* If this is an access to an array, try to get the index as a
constant. If it is (and the array is not a reg array then
this reduces to a signal access and we stay here. If it is
not constant, then give up and do an array index in front
of this part select. */
2007-04-14 06:43:01 +02:00
if (ivl_signal_dimensions(sig) > 0) {
ivl_expr_t ix = ivl_expr_oper1(sube);
2007-04-14 06:43:01 +02:00
if (ivl_signal_type(sig)==IVL_SIT_REG
|| !number_is_immediate(ix, IMM_WID, 0))
2007-04-14 06:43:01 +02:00
return draw_select_array(sube, bit_idx, bit_wid, wid);
/* The index is constant, so we can return to direct
readout with the specific word selected. */
assert(! number_is_unknown(ix));
use_word = get_number_immediate(ix);
}
/* Try the special case that the part is at the beginning of
the signal (or the entire width). Just load the early bits
in one go. */
if (number_is_immediate(bit_idx, IMM_WID, 0)
&& !number_is_unknown(bit_idx)
&& get_number_immediate(bit_idx) == 0
&& (ivl_expr_width(sube) >= bit_wid)) {
res.base = allocate_vector(wid);
res.wid = wid;
assert(res.base);
fprintf(vvp_out, " %%load/v %u, v%p_%u, %u; Only need %u of %u bits\n",
res.base, sig, use_word, bit_wid, bit_wid, ivl_expr_width(sube));
save_signal_lookaside(res.base, sig, use_word, bit_wid);
/* Pad the part select to the desired width. Because of
$signed() this may be signed or unsigned (default). */
pad_expr_in_place(expr, res, bit_wid);
return res;
}
/* Alas, do it the hard way. */
draw_eval_expr_into_integer(bit_idx, 1);
res.base = allocate_vector(wid);
res.wid = wid;
assert(res.base);
lab_x = local_count++;
lab_end = local_count++;
/* If the index is 'bx then we just return 'bx. */
fprintf(vvp_out, " %%jmp/1 T_%d.%d, 4;\n", thread_count, lab_x);
use_wid = res.wid;
if (use_wid > bit_wid)
use_wid = bit_wid;
fprintf(vvp_out, " %%load/x1p %u, v%p_%u, %u;\n",
res.base, sig, use_word, use_wid);
/* Pad the part select to the desired width. Because of
$signed() this may be signed or unsigned (default). */
pad_expr_in_place(expr, res, use_wid);
fprintf(vvp_out, " %%jmp T_%d.%d;\n", thread_count, lab_end);
fprintf(vvp_out, "T_%d.%d ;\n", thread_count, lab_x);
fprintf(vvp_out, " %%mov %u, 2, %u;\n", res.base, res.wid);
fprintf(vvp_out, "T_%d.%d ;\n", thread_count, lab_end);
return res;
}
static void draw_select_signal_dest(ivl_expr_t expr,
ivl_expr_t sube,
ivl_expr_t bit_idx,
struct vector_info dest,
int stuff_ok_flag)
{
struct vector_info tmp;
ivl_signal_t sig = ivl_expr_signal(sube);
/* Special case: If the operand is a signal (not an array) and
the part select is coming from the LSB, and the part select
is no larger then the signal itself, then we can load the
value in place, directly. */
if ((ivl_signal_dimensions(sig) == 0)
&& (ivl_expr_width(sube) >= dest.wid)
&& number_is_immediate(bit_idx, IMM_WID, 0)
&& ! number_is_unknown(bit_idx)
&& get_number_immediate(bit_idx) == 0) {
unsigned use_word = 0;
fprintf(vvp_out, " %%load/v %u, v%p_%u, %u; Select %u out of %u bits\n",
dest.base, sig, use_word, dest.wid,
dest.wid, ivl_expr_width(sube));
return;
}
/* Fallback, just draw the expression and copy the result into
the destination. */
tmp = draw_select_signal(expr, sube, bit_idx, dest.wid, dest.wid);
assert(tmp.wid == dest.wid);
fprintf(vvp_out, " %%mov %u, %u, %u; Move signal select into place\n",
dest.base, tmp.base, dest.wid);
if (tmp.base >= 8) {
save_expression_lookaside(tmp.base, sube, tmp.wid);
clr_vector(tmp);
}
}
static struct vector_info draw_select_unsized_literal(ivl_expr_t expr,
unsigned wid,
int stuff_ok_flag)
{
struct vector_info subv, shiv, res;
ivl_expr_t sube = ivl_expr_oper1(expr);
ivl_expr_t shift = ivl_expr_oper2(expr);
assert(!ivl_expr_sized(sube));
res.wid = wid;
/* Evaluate the sub-expression. */
subv = draw_eval_expr(sube, 0);
/* Special case: Any bit/part select of an unsized constant
zero by an unsigned base is another constant zero, so short
circuit and return the value we know. */
if (subv.base == 0 && !ivl_expr_signed(shift)) {
fprintf(vvp_out, "; Part select of unsized zero with unsigned shift is zero.\n");
subv.wid = wid;
return subv;
}
/* Special case: Any bit/part select of an unsized constant -1
by an unsigned base is another constant -1, so short
circuit and return the value we know. */
if (subv.base == 1 && ivl_expr_signed(sube) && !ivl_expr_signed(shift)) {
fprintf(vvp_out, "; Part select of unsized -1 with unsigned shift is -1.\n");
subv.wid = wid;
return subv;
}
/* Evaluate the bit select base expression. */
shiv = draw_eval_expr(shift, STUFF_OK_XZ);
2005-01-28 06:37:48 +01:00
/* Special case: If the shift is a constant 0, skip the shift
and return the subexpression with the width trimmed down to
the part select width. */
2005-01-28 06:37:48 +01:00
if (shiv.base == 0) {
fprintf(vvp_out, "; Part select with shift==0 skips shift.\n");
2005-01-28 06:37:48 +01:00
assert(subv.wid >= wid);
res.base = subv.base;
return res;
}
/* Special case: If the expression is an unsized zero (and we
know that the shift is signed) then the expression value is
0 if the shift is >= 0, and x otherwise. The trickery in
this special case assumes the output width is 1. */
if (subv.base==0 && wid==1) {
assert(ivl_expr_signed(shift));
if (shiv.base < 4) {
assert(shiv.base != 0);
res.base = 2;
res.wid = wid;
return res;
}
/* Test if the shift is <0 by looking at the sign
bit. If the sign bit is 1, then it is negative and
the result is 1'bx. If the sign bit is 0, then the
result is 1'b0. */
clr_vector(shiv);
res.base = allocate_vector(wid);
res.wid = wid;
fprintf(vvp_out, " %%mov %u, 2, 1;\n", res.base);
fprintf(vvp_out, " %%and %u, %u, 1; x if shift<0, 0 otherwise\n",
res.base, shiv.base+shiv.wid-1);
return res;
}
/* Special case: If the expression is an unsized -1 (and we
know that the shift is signed) then the expression value is
1 if the shift is >= 0, and x otherwise. The trickery in
this special case assumes the output width is 1. */
if (subv.base==1 && ivl_expr_signed(sube) && wid==1) {
assert(ivl_expr_signed(shift));
if (shiv.base < 4) {
assert(shiv.base != 0);
res.base = 2;
res.wid = wid;
return res;
}
/* Test if the shift is <0 by looking at the sign
bit. If the sign bit is 1, then it is negative and
the result is 1'bx. If the sign bit is 0, then the
result is 1'b1. */
clr_vector(shiv);
res.base = allocate_vector(wid);
res.wid = wid;
fprintf(vvp_out, " %%mov %u, 2, 1;\n", res.base);
fprintf(vvp_out, " %%nand %u, %u, 1; x if shift<0, 1 otherwise\n",
res.base, shiv.base+shiv.wid-1);
return res;
}
/* Fall back to doing it the hard way. */
unsigned lab_end = local_count++;
unsigned lab_x = local_count++;
/* Store the bit select base into index register 0, in
preparation for doing a shift. */
if (ivl_expr_signed(shift)) {
fprintf(vvp_out, " %%ix/get/s 0, %u, %u;\n", shiv.base,
shiv.wid);
} else {
fprintf(vvp_out, " %%ix/get 0, %u, %u;\n", shiv.base, shiv.wid);
}
clr_vector(shiv);
/* If we have an undefined index then just produce a 'bx result. */
fprintf(vvp_out, " %%jmp/1 T_%d.%d, 4;\n", thread_count, lab_x);
2010-10-02 20:02:27 +02:00
/* If the subv result is a magic constant, then make a copy in
writable vector space and work from there instead. */
if (subv.base < 4) {
res.base = allocate_vector(subv.wid);
res.wid = subv.wid;
assert(res.base);
fprintf(vvp_out, " %%mov %u, %u, %u;\n", res.base,
subv.base, res.wid);
subv = res;
}
/* If the subv result is narrower then the select width, then
copy it into a wider vector. */
if (subv.wid < wid && ivl_expr_signed(sube)) {
res.base = allocate_vector(wid);
res.wid = wid;
assert(res.base);
fprintf(vvp_out, " %%mov %u, %u, %u; Pad sub-expression to match width\n",
res.base, subv.base, subv.wid);
if (ivl_expr_signed(sube)) {
2010-10-02 20:02:27 +02:00
unsigned idx;
for (idx = subv.wid ; idx < res.wid ; idx += 1) {
fprintf(vvp_out, " %%mov %u, %u, 1;\n",
res.base+idx, subv.base+subv.wid-1);
}
} else {
fprintf(vvp_out, " %%mov %u, 0, %u\n",
res.base+subv.wid, wid-subv.wid);
}
subv = res;
}
if (ivl_expr_signed(sube))
fprintf(vvp_out, " %%shiftr/s/i0 %u, %u; Sign-extending pad\n", subv.base, subv.wid);
else
fprintf(vvp_out, " %%shiftr/i0 %u, %u;\n", subv.base, subv.wid);
fprintf(vvp_out, " %%jmp T_%d.%d;\n", thread_count, lab_end);
fprintf(vvp_out, "T_%d.%d ; Return 'bx value\n", thread_count, lab_x);
fprintf(vvp_out, " %%mov %u, 2, %u;\n", subv.base, wid);
fprintf(vvp_out, " %%jmp T_%d.%d;\n", thread_count, lab_end);
/* DONE */
fprintf(vvp_out, "T_%d.%d ;\n", thread_count, lab_end);
if (subv.wid > wid) {
res.base = subv.base;
res.wid = wid;
subv.base += wid;
subv.wid -= wid;
clr_vector(subv);
} else {
assert(subv.wid == wid);
res = subv;
}
return res;
}
static struct vector_info draw_select_expr(ivl_expr_t expr, unsigned wid,
int stuff_ok_flag)
{
struct vector_info subv, shiv, res;
ivl_expr_t sube = ivl_expr_oper1(expr);
ivl_expr_t shift = ivl_expr_oper2(expr);
int alloc_exclusive = (stuff_ok_flag&STUFF_OK_RO)? 0 : 1;
int cmp;
unsigned lab_l, lab_end;
res.wid = wid;
/* First look for the self expression in the lookaside, and
allocate that if possible. If I find it, then immediately
return that. */
if ( (res.base = allocate_vector_exp(expr, wid, alloc_exclusive)) != 0) {
fprintf(vvp_out, "; Reuse base=%u wid=%u from lookaside.\n",
res.base, wid);
return res;
}
if (ivl_expr_type(sube) == IVL_EX_SIGNAL) {
res = draw_select_signal(expr, sube, shift, ivl_expr_width(expr),
wid);
fprintf(vvp_out, "; Save base=%u wid=%u in lookaside.\n",
res.base, wid);
save_expression_lookaside(res.base, expr, wid);
return res;
}
if (! ivl_expr_sized(sube)) {
res = draw_select_unsized_literal(expr, wid, stuff_ok_flag);
return res;
}
subv = draw_eval_expr(sube, 0);
/* Evaluate the bit select base expression. */
shiv = draw_eval_expr(shift, STUFF_OK_XZ);
/* Special case: If the shift is a constant 0, skip the shift
and return the subexpression with the width trimmed down to
the part select width. */
if (shiv.base == 0) {
fprintf(vvp_out, "; Part select with shift==0 skips shift.\n");
assert(subv.wid >= wid);
res.base = subv.base;
return res;
}
/* Store the bit select base into index register 0, in
preparation for doing a shift. */
if (ivl_expr_signed(shift)) {
fprintf(vvp_out, " %%ix/get/s 0, %u, %u;\n", shiv.base,
shiv.wid);
} else {
fprintf(vvp_out, " %%ix/get 0, %u, %u;\n", shiv.base, shiv.wid);
}
clr_vector(shiv);
/* If the subv result is a magic constant, then make a copy in
2007-02-26 20:49:48 +01:00
writable vector space and work from there instead. */
if (subv.base < 4) {
res.base = allocate_vector(subv.wid);
res.wid = subv.wid;
assert(res.base);
fprintf(vvp_out, " %%mov %u, %u, %u;\n", res.base,
subv.base, res.wid);
subv = res;
}
lab_l = local_count++;
lab_end = local_count++;
/* If we have an undefined index then just produce a 'bx result. */
fprintf(vvp_out, " %%jmp/1 T_%d.%d, 4;\n", thread_count, lab_l);
cmp = allocate_word();
assert(subv.wid >= wid);
/* Determine if we need to shift a 'bx into the top. */
fprintf(vvp_out, " %%ix/load %u, %u, 0;\n", cmp, subv.wid - wid);
fprintf(vvp_out, " %%cmp/ws %u, 0;\n", cmp);
clr_word(cmp);
fprintf(vvp_out, " %%jmp/1 T_%d.%d, 5;\n", thread_count, lab_l);
/* Clear the cmp bit if the two values are equal. */
fprintf(vvp_out, " %%mov 4, 0, 1;\n");
fprintf(vvp_out, " %%shiftr/i0 %u, %u;\n", subv.base, subv.wid);
fprintf(vvp_out, " %%jmp T_%d.%d;\n", thread_count, lab_end);
fprintf(vvp_out, "T_%d.%d ;\n", thread_count, lab_l);
/* Multiply by -1. */
fprintf(vvp_out, " %%ix/mul 0, %u, %u;\n", 0xFFFFFFFF, 0xFFFFFFFF);
fprintf(vvp_out, " %%shiftl/i0 %u, %u;\n", subv.base, subv.wid);
fprintf(vvp_out, "T_%d.%d ;\n", thread_count, lab_end);
if (subv.wid > wid) {
res.base = subv.base;
res.wid = wid;
subv.base += wid;
subv.wid -= wid;
clr_vector(subv);
} else {
assert(subv.wid == wid);
res = subv;
}
if (res.base >= 8) {
fprintf(vvp_out, "; Save expression base=%u wid=%u in lookaside\n",
res.base, wid);
save_expression_lookaside(res.base, expr, wid);
}
2002-09-27 18:33:34 +02:00
return res;
}
static void draw_select_expr_dest(ivl_expr_t expr, struct vector_info dest,
int stuff_ok_flag)
{
struct vector_info tmp;
ivl_expr_t sube = ivl_expr_oper1(expr);
ivl_expr_t shift= ivl_expr_oper2(expr);
/* If the shift expression is not present, then this is really
a pad expression, and that can be handled pretty
easily. Evaluate the subexpression into the destination,
then pad in place. */
if (shift == 0) {
struct vector_info subv;
subv.base = dest.base;
subv.wid = ivl_expr_width(sube);
if (subv.wid > dest.wid)
subv.wid = dest.wid;
draw_eval_expr_dest(sube, subv, stuff_ok_flag);
pad_in_place(dest, subv.wid, ivl_expr_signed(expr));
return;
}
if (ivl_expr_type(sube) == IVL_EX_SIGNAL) {
draw_select_signal_dest(expr, sube, shift, dest, stuff_ok_flag);
return;
}
/* Fallback, is to draw the expression by width, and mov it to
the required dest. */
tmp = draw_select_expr(expr, dest.wid, stuff_ok_flag);
assert(tmp.wid == dest.wid);
fprintf(vvp_out, " %%mov %u, %u, %u; Move select into place\n",
dest.base, tmp.base, dest.wid);
if (tmp.base >= 8) {
save_expression_lookaside(tmp.base, expr, tmp.wid);
clr_vector(tmp);
}
}
static struct vector_info draw_ternary_expr(ivl_expr_t expr, unsigned wid)
2001-05-17 06:37:02 +02:00
{
struct vector_info res, tru, fal, tst;
2001-05-17 06:37:02 +02:00
unsigned lab_true, lab_false, lab_out;
ivl_expr_t cond = ivl_expr_oper1(expr);
ivl_expr_t true_ex = ivl_expr_oper2(expr);
ivl_expr_t false_ex = ivl_expr_oper3(expr);
2001-05-17 06:37:02 +02:00
lab_true = local_count++;
2001-05-17 06:37:02 +02:00
lab_false = local_count++;
lab_out = local_count++;
/* Evaluate the condition expression, and if necessary reduce
it to a single bit. */
tst = draw_eval_expr(cond, STUFF_OK_XZ|STUFF_OK_RO);
if ((tst.base >= 4) && (tst.wid > 1)) {
struct vector_info tmp;
2001-05-17 06:37:02 +02:00
2002-07-12 20:10:45 +02:00
fprintf(vvp_out, " %%or/r %u, %u, %u;\n",
tst.base, tst.base, tst.wid);
tmp = tst;
tmp.base += 1;
tmp.wid -= 1;
clr_vector(tmp);
tst.wid = 1;
2002-07-12 20:10:45 +02:00
}
fprintf(vvp_out, " %%jmp/0 T_%d.%d, %u;\n",
thread_count, lab_true, tst.base);
tru = draw_eval_expr_wid(true_ex, wid, 0);
/* The true result must be in a writable register, because the
blend might want to manipulate it. */
if (tru.base < 4) {
struct vector_info tmp;
tmp.base = allocate_vector(wid);
tmp.wid = wid;
assert(tmp.base);
fprintf(vvp_out, " %%mov %u, %u, %u;\n",
tmp.base, tru.base, wid);
tru = tmp;
}
2001-05-17 06:37:02 +02:00
fprintf(vvp_out, " %%jmp/1 T_%d.%d, %u;\n",
thread_count, lab_out, tst.base);
2001-05-17 06:37:02 +02:00
clear_expression_lookaside();
fprintf(vvp_out, "T_%d.%d ; End of true expr.\n",
thread_count, lab_true);
fal = draw_eval_expr_wid(false_ex, wid, 0);
fprintf(vvp_out, " %%jmp/0 T_%d.%d, %u;\n",
thread_count, lab_false, tst.base);
fprintf(vvp_out, " ; End of false expr.\n");
clr_vector(tst);
clr_vector(fal);
2001-05-17 06:37:02 +02:00
fprintf(vvp_out, " %%blend %u, %u, %u; Condition unknown.\n",
tru.base, fal.base, wid);
fprintf(vvp_out, " %%jmp T_%d.%d;\n",
2003-06-17 00:14:15 +02:00
thread_count, lab_out);
2001-05-17 06:37:02 +02:00
fprintf(vvp_out, "T_%d.%d ;\n", thread_count, lab_false);
fprintf(vvp_out, " %%mov %u, %u, %u; Return false value\n",
tru.base, fal.base, wid);
2001-05-17 06:37:02 +02:00
/* This is the out label. */
2001-05-17 06:37:02 +02:00
fprintf(vvp_out, "T_%d.%d ;\n", thread_count, lab_out);
clear_expression_lookaside();
2001-05-17 06:37:02 +02:00
res = tru;
2002-09-27 18:33:34 +02:00
if (res.base >= 8)
save_expression_lookaside(res.base, expr, wid);
2002-09-27 18:33:34 +02:00
2001-05-17 06:37:02 +02:00
return res;
}
static struct vector_info draw_sfunc_expr(ivl_expr_t expr, unsigned wid)
2001-05-20 03:02:55 +02:00
{
unsigned parm_count = ivl_expr_parms(expr);
2001-05-20 03:02:55 +02:00
struct vector_info res;
2001-07-07 22:20:10 +02:00
/* If the function has no parameters, then use this short-form
to draw the statement. */
if (parm_count == 0) {
assert(ivl_expr_value(expr) == IVL_VT_LOGIC
|| ivl_expr_value(expr) == IVL_VT_BOOL);
2001-07-07 22:20:10 +02:00
res.base = allocate_vector(wid);
res.wid = wid;
assert(res.base);
fprintf(vvp_out, " %%vpi_func %u %u \"%s\", %u, %u;\n",
ivl_file_table_index(ivl_expr_file(expr)),
ivl_expr_lineno(expr), ivl_expr_name(expr),
res.base, res.wid);
2001-07-07 22:20:10 +02:00
return res;
}
res = draw_vpi_func_call(expr, wid);
2001-07-07 22:20:10 +02:00
2002-09-27 18:33:34 +02:00
/* New basic block starts after VPI calls. */
clear_expression_lookaside();
2001-05-20 03:02:55 +02:00
return res;
}
static struct vector_info draw_unary_expr(ivl_expr_t expr, unsigned wid)
2001-03-29 04:52:39 +02:00
{
struct vector_info res;
ivl_expr_t sub = ivl_expr_oper1(expr);
const char *rop = 0;
int word, inv = 0;
switch (ivl_expr_opcode(expr)) {
case '&': rop = "and"; break;
case '|': rop = "or"; break;
case '^': rop = "xor"; break;
case 'A': rop = "nand"; break;
case 'N': rop = "nor"; break;
case 'X': rop = "xnor"; break;
}
2001-03-29 04:52:39 +02:00
switch (ivl_expr_opcode(expr)) {
2001-03-29 04:52:39 +02:00
case '~':
res = draw_eval_expr_wid(sub, wid, STUFF_OK_XZ);
switch (res.base) {
case 0:
res.base = 1;
break;
case 1:
res.base = 0;
break;
case 2:
case 3:
res.base = 2;
break;
default:
fprintf(vvp_out, " %%inv %u, %u;\n", res.base, res.wid);
break;
}
2001-03-29 04:52:39 +02:00
break;
2001-09-29 06:37:44 +02:00
case '-':
/* Unary minus is implemented by generating the 2's
complement of the number. That is the 1's complement
(bitwise invert) with a 1 added in. Note that the
%sub subtracts -1 (1111...) to get %add of +1. */
res = draw_eval_expr_wid(sub, wid, STUFF_OK_XZ);
2001-09-29 06:37:44 +02:00
switch (res.base) {
case 0:
res.base = 0;
break;
case 1:
res.base = 1;
break;
case 2:
case 3:
res.base = 2;
break;
default:
fprintf(vvp_out, " %%inv %u, %u;\n", res.base, res.wid);
2003-10-01 19:44:20 +02:00
fprintf(vvp_out, " %%addi %u, 1, %u;\n",res.base,res.wid);
2001-09-29 06:37:44 +02:00
break;
}
break;
2001-04-01 23:47:29 +02:00
case '!':
res = draw_eval_expr(sub, STUFF_OK_XZ);
if (res.wid > 1) {
/* a ! on a vector is implemented with a reduction
nor. Generate the result into the first bit of
the input vector and free the rest of the
vector. */
struct vector_info tmp;
assert(res.base >= 4);
tmp.base = res.base+1;
tmp.wid = res.wid - 1;
fprintf(vvp_out, " %%nor/r %u, %u, %u;\n",
res.base, res.base, res.wid);
clr_vector(tmp);
res.wid = 1;
2002-05-07 05:49:58 +02:00
} else switch (res.base) {
case 0:
res.base = 1;
break;
case 1:
res.base = 0;
break;
case 2:
case 3:
res.base = 2;
break;
default:
fprintf(vvp_out, " %%inv %u, 1;\n", res.base);
break;
}
2001-11-19 05:25:46 +01:00
/* If the result needs to be bigger then the calculated
value, then write it into a padded vector. */
if (res.wid < wid) {
struct vector_info tmp;
tmp.base = allocate_vector(wid);
tmp.wid = wid;
if (tmp.base == 0) {
fprintf(stderr, "%s:%u: vvp.tgt error: "
"Unable to allocate %u thread bits "
"to pad unary expression result.\n",
ivl_expr_file(expr), ivl_expr_lineno(expr),
wid);
vvp_errors += 1;
}
2001-11-19 05:25:46 +01:00
fprintf(vvp_out, " %%mov %u, %u, %u;\n",
tmp.base, res.base, res.wid);
fprintf(vvp_out, " %%movi %u, 0, %u;\n",
2001-11-19 05:25:46 +01:00
tmp.base+res.wid, tmp.wid-res.wid);
clr_vector(res);
res = tmp;
}
break;
case 'N':
case 'A':
case 'X':
inv = 1;
case '&':
case '|':
case '^':
res = draw_eval_expr(sub, 0);
2001-04-01 23:47:29 +02:00
if (res.wid > 1) {
struct vector_info tmp;
2001-11-19 05:25:46 +01:00
/* If the previous result is in the constant area
(and is a vector) then copy it out into some
temporary space. */
if (res.base < 4) {
tmp.base = allocate_vector(res.wid);
tmp.wid = res.wid;
assert(res.base);
2001-11-19 05:25:46 +01:00
fprintf(vvp_out, " %%mov %u, %u, %u;\n",
tmp.base, res.base, res.wid);
res = tmp;
}
2001-04-01 23:47:29 +02:00
tmp.base = res.base+1;
tmp.wid = res.wid - 1;
fprintf(vvp_out, " %%%s/r %u, %u, %u;\n",
rop,
2001-04-01 23:47:29 +02:00
res.base, res.base, res.wid);
clr_vector(tmp);
res.wid = 1;
} else if (inv) {
2001-11-19 05:25:46 +01:00
assert(res.base >= 4);
2001-04-01 23:47:29 +02:00
fprintf(vvp_out, " %%inv %u, 1;\n", res.base);
} else {
/* We need to convert a 1'bz to 1'bx. */
assert(res.base >= 4);
fprintf(vvp_out, " %%inv %u, 1;\n", res.base);
fprintf(vvp_out, " %%inv %u, 1;\n", res.base);
2001-04-01 23:47:29 +02:00
}
2001-11-19 05:25:46 +01:00
/* If the result needs to be bigger then the calculated
value, then write it into a passed vector. */
if (res.wid < wid) {
struct vector_info tmp;
tmp.base = allocate_vector(wid);
tmp.wid = wid;
assert(res.base);
2001-11-19 05:25:46 +01:00
fprintf(vvp_out, " %%mov %u, %u, %u;\n",
tmp.base, res.base, res.wid);
fprintf(vvp_out, " %%movi %u, 0, %u;\n",
2001-11-19 05:25:46 +01:00
tmp.base+res.wid, tmp.wid-res.wid);
clr_vector(res);
res = tmp;
}
2001-04-01 23:47:29 +02:00
break;
case 'm': /* abs() */
res = draw_eval_expr_wid(sub, wid, 0);
if (!ivl_expr_signed(sub))
break;
if (res.base == 0 || res.base == 2 || res.base == 3)
break;
/* Handle the special case of a -1 constant. Make the
result a 1. */
if (res.base == 1) {
res.base = allocate_vector(wid);
fprintf(vvp_out, " %%movi %d, 1, %u;\n",
res.base, res.wid);
break;
}
fprintf(vvp_out, " %%cmpi/s %d, 0, %u;\n", res.base, res.wid);
fprintf(vvp_out, " %%jmp/0xz T_%u.%u, 5;\n", thread_count,
local_count);
fprintf(vvp_out, " %%inv %d, %u;\n", res.base, res.wid);
fprintf(vvp_out, " %%addi %d, 1, %u;\n", res.base, res.wid);
fprintf(vvp_out, "T_%u.%u ;\n", thread_count, local_count);
local_count += 1;
break;
case '2': /* Cast logic to bool */
assert(ivl_expr_value(sub) == IVL_VT_LOGIC);
res = draw_eval_expr_wid(sub, wid, 0);
/* Handle special case that value is 0 or 1. */
if (res.base == 0 || res.base == 1)
break;
if (res.base == 2 || res.base == 2) {
res.base = 0;
break;
}
fprintf(vvp_out, " %%cast2 %d, %d, %u;\n", res.base, res.base, res.wid);
break;
case 'i': /* Cast a real value to an integer. */
assert(ivl_expr_value(sub) == IVL_VT_REAL);
word = draw_eval_real(sub);
res.base = allocate_vector(wid);
res.wid = wid;
fprintf(vvp_out, " %%cvt/vr %u, %u, %u;\n", res.base, word,
res.wid);
clr_word(word);
break;
case 'r': /* Handled in eval_real.c. */
fprintf(stderr, "vvp.tgt error: integer -> real cast in integer "
"context.\n");
assert(0);
break;
2001-03-29 04:52:39 +02:00
default:
fprintf(stderr, "vvp.tgt error: unhandled unary operator: %c\n",
ivl_expr_opcode(expr));
2001-03-29 04:52:39 +02:00
assert(0);
}
2002-09-27 18:33:34 +02:00
if (res.base >= 8)
save_expression_lookaside(res.base, expr, wid);
2002-09-27 18:33:34 +02:00
2001-03-29 04:52:39 +02:00
return res;
}
/*
* Sometimes we know ahead of time where we want the expression value
* to go. In that case, call this function. It will check to see if
* the expression can be preplaced, and if so it will evaluate it in
* place.
*/
static void draw_eval_expr_dest(ivl_expr_t expr, struct vector_info dest,
int stuff_ok_flag)
{
struct vector_info tmp;
switch (ivl_expr_type(expr)) {
case IVL_EX_SIGNAL:
draw_signal_dest(expr, dest, -1, 0L);
return;
case IVL_EX_SELECT:
draw_select_expr_dest(expr, dest, stuff_ok_flag);
return;
default:
break;
}
/* Fallback, is to draw the expression by width, and move it to
the required dest. */
tmp = draw_eval_expr_wid(expr, dest.wid, stuff_ok_flag);
assert(tmp.wid == dest.wid);
/* If the replication is 0 we can have a zero width, so skip it. */
if (dest.wid) fprintf(vvp_out, " %%mov %u, %u, %u;\n",
dest.base, tmp.base, dest.wid);
if (tmp.base >= 8)
save_expression_lookaside(tmp.base, expr, tmp.wid);
clr_vector(tmp);
}
struct vector_info draw_eval_expr_wid(ivl_expr_t expr, unsigned wid,
int stuff_ok_flag)
{
struct vector_info res;
switch (ivl_expr_type(expr)) {
default:
case IVL_EX_NONE:
fprintf(stderr, "%s:%u: vvp.tgt error: unhandled expr. type: "
"%u at %s:%d\n", ivl_expr_file(expr), ivl_expr_lineno(expr),
ivl_expr_type(expr), __FILE__, __LINE__);
exit(1);
res.base = 0;
res.wid = 0;
break;
case IVL_EX_EVENT:
fprintf(stderr, "%s:%u: vvp.tgt error: A named event is not "
"handled in this context (expression).\n",
ivl_expr_file(expr), ivl_expr_lineno(expr));
exit(1);
break;
case IVL_EX_STRING:
res = draw_string_expr(expr, wid);
break;
case IVL_EX_BINARY:
res = draw_binary_expr(expr, wid, stuff_ok_flag);
break;
case IVL_EX_CONCAT:
res = draw_concat_expr(expr, wid, stuff_ok_flag);
break;
case IVL_EX_NUMBER:
res = draw_number_expr(expr, wid);
break;
case IVL_EX_REALNUM:
res = draw_realnum_expr(expr, wid);
break;
case IVL_EX_SELECT:
if (ivl_expr_oper2(expr) == 0)
res = draw_pad_expr(expr, wid);
else
res = draw_select_expr(expr, wid, stuff_ok_flag);
break;
case IVL_EX_SIGNAL:
res = draw_signal_expr(expr, wid, stuff_ok_flag);
break;
2001-03-29 04:52:39 +02:00
2001-05-17 06:37:02 +02:00
case IVL_EX_TERNARY:
res = draw_ternary_expr(expr, wid);
2001-05-17 06:37:02 +02:00
break;
2001-05-20 03:02:55 +02:00
case IVL_EX_SFUNC:
res = draw_sfunc_expr(expr, wid);
2001-05-20 03:02:55 +02:00
break;
case IVL_EX_UFUNC:
res = draw_ufunc_expr(expr, wid);
break;
2001-03-29 04:52:39 +02:00
case IVL_EX_UNARY:
res = draw_unary_expr(expr, wid);
2001-03-29 04:52:39 +02:00
break;
}
return res;
}
struct vector_info draw_eval_expr(ivl_expr_t expr, int stuff_ok_flag)
{
return draw_eval_expr_wid(expr, ivl_expr_width(expr), stuff_ok_flag);
}