iverilog/tgt-vvp/eval_real.c

573 lines
14 KiB
C

/*
* Copyright (c) 2003-2013 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.
*/
/*
* This file includes functions for evaluating REAL expressions.
*/
# include "vvp_priv.h"
# include <string.h>
# include <stdlib.h>
# include <math.h>
# include <assert.h>
# include <stdbool.h>
static unsigned long word_alloc_mask = 0x0f;
int allocate_word()
{
int res = 4;
int max = WORD_COUNT;
while (res < max && (1U << res) & word_alloc_mask)
res += 1;
if (res >= max) {
fprintf(stderr, "vvp.tgt error: Thread words exhausted.\n");
exit(1);
}
word_alloc_mask |= 1U << res;
return res;
}
void clr_word(int res)
{
int max = WORD_COUNT;
assert(res < max);
word_alloc_mask &= ~ (1U << res);
}
static void draw_binary_real(ivl_expr_t expr)
{
switch (ivl_expr_opcode(expr)) {
case 'E':
case 'N':
case 'l':
case 'r':
case 'R':
case '&':
case '|':
case '^':
case 'A':
case 'O':
case 'X':
/* These should be caught in draw_eval_real(). */
assert(0);
}
draw_eval_real(ivl_expr_oper1(expr));
draw_eval_real(ivl_expr_oper2(expr));
switch (ivl_expr_opcode(expr)) {
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;
case 'p':
fprintf(vvp_out, " %%pow/wr;\n");
break;
case 'm':
fprintf(vvp_out, " %%min/wr;\n");
break;
case 'M':
fprintf(vvp_out, " %%max/wr;\n");
break;
default:
fprintf(stderr, "XXXX draw_binary_real(%c)\n",
ivl_expr_opcode(expr));
assert(0);
}
}
static void draw_number_real(ivl_expr_t expr)
{
unsigned int idx;
const char*bits = ivl_expr_bits(expr);
unsigned wid = ivl_expr_width(expr);
unsigned long mant = 0;
int vexp = 0x1000;
/* If this is a negative number, then arrange for the 2's
complement to be calculated as we scan through the
value. Real values are sign-magnitude, and this negation
gets us a magnitude. */
int negate = 0;
int carry = 0;
if (ivl_expr_signed(expr) && (bits[wid-1] == '1')) {
negate = 1;
carry = 1;
}
for (idx = 0 ; idx < wid && idx < IMM_WID ; idx += 1) {
int cur_bit = bits[idx] == '1'? 1 : 0;
if (negate) {
cur_bit ^= 1;
cur_bit += carry;
carry = (cur_bit >> 1) & 1;
cur_bit &= 1;
}
if (cur_bit) mant |= 1 << idx;
}
for ( ; idx < wid ; idx += 1) {
if (ivl_expr_signed(expr) && (bits[idx] == bits[IMM_WID-1]))
continue;
if (bits[idx] == '0')
continue;
fprintf(stderr, "vvp.tgt error: mantissa doesn't fit!\n");
assert(0);
}
/* If required, add in a sign bit. */
if (negate)
vexp |= 0x4000;
fprintf(vvp_out, " %%pushi/real %lu, %d; load(num)= %c%lu (wid=%u)\n",
mant, vexp, (vexp&0x4000)? '-' : '+', mant, wid);
}
static void draw_property_real(ivl_expr_t expr)
{
ivl_signal_t sig = ivl_expr_signal(expr);
unsigned pidx = ivl_expr_property_idx(expr);
fprintf(vvp_out, " %%load/obj v%p_0;\n", sig);
fprintf(vvp_out, " %%prop/r %u;\n", pidx);
}
static void draw_realnum_real(ivl_expr_t expr)
{
double value = ivl_expr_dvalue(expr);
double fract;
int expo, vexp;
unsigned long mant;
int sign = 0;
/* Handle the special case that the value is +-inf. */
if (isinf(value)) {
if (value > 0)
fprintf(vvp_out, " %%pushi/real 0, %d; load=+inf\n",
0x3fff);
else
fprintf(vvp_out, " %%pushi/real 0, %d; load=-inf\n",
0x7fff);
return;
}
/* Handle the special case that the value is NaN. */
if (value != value) {
fprintf(vvp_out, " %%pushi/real 1, %d; load=NaN\n",
0x3fff);
return;
}
if (value < 0) {
sign = 0x4000;
value *= -1;
}
fract = frexp(value, &expo);
fract = ldexp(fract, 31);
mant = fract;
expo -= 31;
vexp = expo + 0x1000;
assert(vexp >= 0);
assert(vexp < 0x2000);
vexp += sign;
fprintf(vvp_out, " %%pushi/real %lu, %d; load=%#g\n",
mant, vexp, ivl_expr_dvalue(expr));
/* Capture the residual bits, if there are any. Note that an
IEEE754 mantissa has 52 bits, 31 of which were accounted
for already. */
fract -= floor(fract);
fract = ldexp(fract, 22);
mant = fract;
expo -= 22;
vexp = expo + 0x1000;
assert(vexp >= 0);
assert(vexp < 0x2000);
vexp += sign;
if (mant != 0) {
fprintf(vvp_out, " %%pushi/real %lu, %d; load=%#g\n",
mant, vexp, ivl_expr_dvalue(expr));
fprintf(vvp_out, " %%add/wr;\n");
}
}
/*
* The real value of a logic expression is the integer value of the
* expression converted to real.
*/
static void draw_real_logic_expr(ivl_expr_t expr, int stuff_ok_flag)
{
struct vector_info sv = draw_eval_expr(expr, stuff_ok_flag);
const char*sign_flag = ivl_expr_signed(expr)? "/s" : "";
if (sv.wid > 64) {
fprintf(vvp_out, " %%cvt/rv%s %u, %u;\n",
sign_flag, sv.base, sv.wid);
} else {
int res = allocate_word();
fprintf(vvp_out, " %%ix/get%s %d, %u, %u;\n",
sign_flag, res, sv.base, sv.wid);
if (ivl_expr_signed(expr))
fprintf(vvp_out, " %%cvt/rs %d;\n", res);
else
fprintf(vvp_out, " %%cvt/ru %d;\n", res);
clr_word(res);
}
clr_vector(sv);
}
static void draw_select_real(ivl_expr_t expr)
{
/* The sube references the expression to be selected from. */
ivl_expr_t sube = ivl_expr_oper1(expr);
/* This is the select expression */
ivl_expr_t shift= ivl_expr_oper2(expr);
/* Assume the sub-expression is a signal */
ivl_signal_t sig = ivl_expr_signal(sube);
assert(ivl_signal_data_type(sig) == IVL_VT_DARRAY);
draw_eval_expr_into_integer(shift, 3);
fprintf(vvp_out, " %%load/dar/r v%p_0;\n", sig);
}
static void draw_sfunc_real(ivl_expr_t expr)
{
switch (ivl_expr_value(expr)) {
case IVL_VT_REAL:
if (ivl_expr_parms(expr) == 0) {
fprintf(vvp_out, " %%vpi_func/r %u %u \"%s\" {0 0};\n",
ivl_file_table_index(ivl_expr_file(expr)),
ivl_expr_lineno(expr), ivl_expr_name(expr));
} else {
draw_vpi_rfunc_call(expr);
}
break;
case IVL_VT_VECTOR:
/* If the value of the sfunc is a vector, then evaluate
it as a vector, then convert the result to a real
(via an index register) for the result. */
draw_real_logic_expr(expr, 0);
break;
default:
assert(0);
}
}
static void draw_signal_real_real(ivl_expr_t expr)
{
ivl_signal_t sig = ivl_expr_signal(expr);
if (ivl_signal_dimensions(sig) == 0) {
fprintf(vvp_out, " %%load/real v%p_0;\n", sig);
return;
}
ivl_expr_t word_ex = ivl_expr_oper1(expr);
int word_ix = allocate_word();
draw_eval_expr_into_integer(word_ex, word_ix);
fprintf(vvp_out, " %%load/ar v%p, %d;\n", sig, word_ix);
clr_word(word_ix);
}
static void draw_signal_real(ivl_expr_t expr)
{
ivl_signal_t sig = ivl_expr_signal(expr);
switch (ivl_signal_data_type(sig)) {
case IVL_VT_LOGIC:
draw_real_logic_expr(expr, 0);
return;
case IVL_VT_REAL:
draw_signal_real_real(expr);
return;
default:
fprintf(stderr, "vvp.tgt error: signal_data_type=%d\n",
ivl_signal_data_type(sig));
assert(0);
return;
}
}
/* If we have nested ternary operators they are likely tail recursive.
* This code is structured to allow this recursion without overflowing
* the available thread words. */
static void draw_ternary_real(ivl_expr_t expr)
{
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);
struct vector_info tst;
unsigned lab_true = local_count++;
unsigned lab_out = local_count++;
/* Evaluate the ternary condition. */
tst = draw_eval_expr(cond, STUFF_OK_XZ|STUFF_OK_RO);
if ((tst.base >= 4) && (tst.wid > 1)) {
struct vector_info tmp;
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;
}
/* Evaluate the true expression second. */
fprintf(vvp_out, " %%jmp/1 T_%u.%u, %u;\n",
thread_count, lab_true, tst.base);
/* Evaluate the false expression. */
draw_eval_real(false_ex);
fprintf(vvp_out, " %%jmp/0 T_%u.%u, %u; End of false expr.\n",
thread_count, lab_out, tst.base);
/* If the conditional is undefined then blend the real words. */
draw_eval_real(true_ex);
fprintf(vvp_out, " %%blend/wr;\n");
fprintf(vvp_out, " %%jmp T_%u.%u; End of blend\n",
thread_count, lab_out);
/* Evaluate the true expression. */
fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_true);
draw_eval_real(true_ex);
/* This is the out label. */
fprintf(vvp_out, "T_%u.%u ;\n", thread_count, lab_out);
clr_vector(tst);
}
static void increment(ivl_expr_t e, bool pre)
{
ivl_signal_t sig = ivl_expr_signal(e);
fprintf(vvp_out, " %%load/real v%p_0;\n", sig);
if (!pre) fprintf(vvp_out, " %%dup/real;\n");
fprintf(vvp_out, " %%pushi/real 1, 0x1000;\n");
fprintf(vvp_out, " %%add/wr;\n");
if ( pre) fprintf(vvp_out, " %%dup/real;\n");
fprintf(vvp_out, " %%store/real v%p_0;\n", sig);
}
static void decrement(ivl_expr_t e, bool pre)
{
ivl_signal_t sig = ivl_expr_signal(e);
fprintf(vvp_out, " %%load/real v%p_0;\n", sig);
if (!pre) fprintf(vvp_out, " %%dup/real;\n");
fprintf(vvp_out, " %%pushi/real 1, 0x1000;\n");
fprintf(vvp_out, " %%sub/wr;\n");
if ( pre) fprintf(vvp_out, " %%dup/real;\n");
fprintf(vvp_out, " %%store/real v%p_0;\n", sig);
}
static void draw_unary_real(ivl_expr_t expr)
{
ivl_expr_t sube;
/* If the opcode is a ~ or a ! then the sub expression must not be
* a real expression, so use vector evaluation and then convert
* that result to a real value. */
if ((ivl_expr_opcode(expr) == '~') || (ivl_expr_opcode(expr) == '!')) {
draw_real_logic_expr(expr, STUFF_OK_XZ);
return;
}
sube = ivl_expr_oper1(expr);
if (ivl_expr_opcode(expr) == 'r') { /* Cast an integer value to a real. */
struct vector_info res;
const char *suffix = "";
assert(ivl_expr_value(sube) != IVL_VT_REAL);
res = draw_eval_expr(sube, 1);
if (ivl_expr_signed(sube)) suffix = "/s";
fprintf(vvp_out, " %%cvt/rv%s %u, %u;\n", suffix, res.base, res.wid);
clr_vector(res);
return;
}
if (ivl_expr_opcode(expr) == '+') {
draw_eval_real(sube);
return;
}
if (ivl_expr_opcode(expr) == '-') {
fprintf(vvp_out, " %%pushi/real 0, 0; load 0.0\n");
draw_eval_real(sube);
fprintf(vvp_out, " %%sub/wr;\n");
return;
}
if (ivl_expr_opcode(expr) == 'm') { /* abs() */
draw_eval_real(sube);
fprintf(vvp_out, " %%abs/wr;\n");
return;
}
if (ivl_expr_opcode(expr) == 'v') { /* Handled in eval_expr.c. */
fprintf(stderr, "vvp.tgt error: real -> integer cast in real "
"context.\n");
assert(0);
}
switch (ivl_expr_opcode(expr)) {
case 'I':
increment(sube, true);
return;
case 'i':
increment(sube, false);
return;
case 'D':
decrement(sube, true);
return;
case 'd':
decrement(sube, false);
return;
}
fprintf(stderr, "vvp.tgt error: unhandled real unary operator: %c.\n",
ivl_expr_opcode(expr));
assert(0);
}
void draw_eval_real(ivl_expr_t expr)
{
/* If this expression/sub-expression is not real then we need
* to evaluate it as a bit value and then convert the bit based
* result to a real value. This is required to get integer
* division to work correctly. */
if (ivl_expr_value(expr) != IVL_VT_REAL) {
draw_real_logic_expr(expr, STUFF_OK_XZ);
return;
}
switch (ivl_expr_type(expr)) {
case IVL_EX_BINARY:
draw_binary_real(expr);
break;
case IVL_EX_NUMBER:
draw_number_real(expr);
break;
case IVL_EX_PROPERTY:
draw_property_real(expr);
break;
case IVL_EX_REALNUM:
draw_realnum_real(expr);
break;
case IVL_EX_SELECT:
draw_select_real(expr);
break;
case IVL_EX_SFUNC:
draw_sfunc_real(expr);
break;
case IVL_EX_SIGNAL:
draw_signal_real(expr);
break;
case IVL_EX_TERNARY:
draw_ternary_real(expr);
break;
case IVL_EX_UFUNC:
draw_ufunc_real(expr);
break;
case IVL_EX_UNARY:
draw_unary_real(expr);
break;
default:
if (ivl_expr_value(expr) == IVL_VT_VECTOR) {
struct vector_info sv = draw_eval_expr(expr, 0);
const char*sign_flag = ivl_expr_signed(expr)? "/s" : "";
clr_vector(sv);
int res = allocate_word();
fprintf(vvp_out, " %%ix/get%s %d, %u, %u;\n",
sign_flag, res, sv.base, sv.wid);
fprintf(vvp_out, " %%cvt/rs %d;\n", res);
clr_word(res);
} else {
fprintf(stderr, "XXXX Evaluate real expression (%d)\n",
ivl_expr_type(expr));
fprintf(vvp_out, " ; XXXX Evaluate real expression (%d)\n",
ivl_expr_type(expr));
return;
}
break;
}
}