Merge branch 'master' into vec4-stack

Conflicts:
	netmisc.cc
	tgt-vvp/eval_expr.c
	vvp/vthread.cc
This commit is contained in:
Stephen Williams 2014-03-01 09:02:14 -08:00
commit 7ceb18fb37
19 changed files with 374 additions and 245 deletions

View File

@ -8,7 +8,7 @@ Icarus Verilog is intended to compile ALL of the Verilog HDL as
described in the IEEE-1364 standard. Of course, it's not quite there described in the IEEE-1364 standard. Of course, it's not quite there
yet. It does currently handle a mix of structural and behavioral yet. It does currently handle a mix of structural and behavioral
constructs. For a view of the current state of Icarus Verilog, see its constructs. For a view of the current state of Icarus Verilog, see its
home page at <http://www.icarus.com/eda/verilog>. home page at <http://iverilog.icarus.com/>.
Icarus Verilog is not aimed at being a simulator in the traditional Icarus Verilog is not aimed at being a simulator in the traditional
sense, but a compiler that generates code employed by back-end sense, but a compiler that generates code employed by back-end

View File

@ -1,7 +1,7 @@
#ifndef __compiler_H #ifndef __compiler_H
#define __compiler_H #define __compiler_H
/* /*
* Copyright (c) 1999-2013 Stephen Williams (steve@icarus.com) * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -36,6 +36,11 @@
*/ */
extern unsigned integer_width; extern unsigned integer_width;
/*
* The width_cap is the width limit for unsized expressions.
*/
extern unsigned width_cap;
/* /*
* This is the maximum number of recursive module loops allowed within * This is the maximum number of recursive module loops allowed within
* a generate block. * a generate block.

View File

@ -4,7 +4,7 @@
%{ %{
/* /*
* Copyright (c) 2001-2009 Stephen Williams (steve@icarus.com) * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -92,6 +92,8 @@ int cmdfile_stack_ptr = 0;
"+vhdl-libdir+" { BEGIN(PLUS_ARGS); return TOK_VHDL_LIBDIR; } "+vhdl-libdir+" { BEGIN(PLUS_ARGS); return TOK_VHDL_LIBDIR; }
"+width-cap+" { BEGIN(PLUS_ARGS); return TOK_WIDTH_CAP; }
/* If it is not any known plus-flag, return the generic form. */ /* If it is not any known plus-flag, return the generic form. */
"+"[^\n \t\b\f\r+]* { "+"[^\n \t\b\f\r+]* {
cflval.text = strdup(yytext); cflval.text = strdup(yytext);

View File

@ -1,6 +1,6 @@
%{ %{
/* /*
* Copyright (c) 2001-2011 Stephen Williams (steve@icarus.com) * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -61,6 +61,7 @@ static void translate_file_name(char*text)
%token TOK_Da TOK_Dc TOK_Dv TOK_Dy %token TOK_Da TOK_Dc TOK_Dv TOK_Dy
%token TOK_DEFINE TOK_INCDIR TOK_INTEGER_WIDTH TOK_LIBDIR TOK_LIBDIR_NOCASE %token TOK_DEFINE TOK_INCDIR TOK_INTEGER_WIDTH TOK_LIBDIR TOK_LIBDIR_NOCASE
%token TOK_LIBEXT TOK_PARAMETER TOK_TIMESCALE TOK_VHDL_WORK TOK_VHDL_LIBDIR %token TOK_LIBEXT TOK_PARAMETER TOK_TIMESCALE TOK_VHDL_WORK TOK_VHDL_LIBDIR
%token TOK_WIDTH_CAP
%token <text> TOK_PLUSARG TOK_PLUSWORD TOK_STRING %token <text> TOK_PLUSARG TOK_PLUSWORD TOK_STRING
%% %%
@ -191,6 +192,13 @@ item
free(tmp); free(tmp);
} }
| TOK_WIDTH_CAP TOK_PLUSARG
{ char*tmp = substitutions($2);
free($2);
width_cap = strtoul(tmp,0,10);
free(tmp);
}
/* The +<word> tokens that are not otherwise matched, are /* The +<word> tokens that are not otherwise matched, are
ignored. The skip_args rule arranges for all the argument words ignored. The skip_args rule arranges for all the argument words
to be consumed. */ to be consumed. */

View File

@ -1,7 +1,7 @@
#ifndef __globals_H #ifndef __globals_H
#define __globals_H #define __globals_H
/* /*
* Copyright (c) 2000-2009 Stephen Williams (steve@icarus.com) * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -24,6 +24,9 @@
/* This is the integer-width argument that will be passed to ivl. */ /* This is the integer-width argument that will be passed to ivl. */
extern unsigned integer_width; extern unsigned integer_width;
/* This is the width-cap argument that will be passed to ivl. */
extern unsigned width_cap;
extern const char*vhdlpp_work; extern const char*vhdlpp_work;
extern const char**vhdlpp_libdir; extern const char**vhdlpp_libdir;
extern unsigned vhdlpp_libdir_cnt; extern unsigned vhdlpp_libdir_cnt;

View File

@ -1,4 +1,4 @@
.TH iverilog 1 "April 2nd, 2013" "" "Version %M.%m.%n %E" .TH iverilog 1 "February 26th, 2014" "" "Version %M.%m.%n %E"
.SH NAME .SH NAME
iverilog - Icarus Verilog compiler iverilog - Icarus Verilog compiler
@ -462,6 +462,12 @@ This allows the programmer to select the width for integer variables
in the Verilog source. The default is 32, the value can be any desired in the Verilog source. The default is 32, the value can be any desired
integer value. integer value.
.TP 8
.B +width-cap+\fIvalue\fP
This allows the programmer to select the width cap for unsized expressions.
If the calculated width for an unsized expression exceeds this value, the
compiler will issue a warning and limit the expression width to this value.
.SH "VARIABLES IN COMMAND FILES" .SH "VARIABLES IN COMMAND FILES"
In certain cases, iverilog supports variables in command files. These In certain cases, iverilog supports variables in command files. These
@ -515,7 +521,7 @@ Tips on using, debugging, and developing the compiler can be found at
.SH COPYRIGHT .SH COPYRIGHT
.nf .nf
Copyright \(co 2002\-2011 Stephen Williams Copyright \(co 2002\-2014 Stephen Williams
This document can be freely redistributed according to the terms of the This document can be freely redistributed according to the terms of the
GNU General Public License version 2.0 GNU General Public License version 2.0

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2000-2013 Stephen Williams (steve@icarus.com) * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -141,6 +141,8 @@ char warning_flags[16] = "n";
unsigned integer_width = 32; unsigned integer_width = 32;
unsigned width_cap = 65536;
char*mod_list = 0; char*mod_list = 0;
char*command_filename = 0; char*command_filename = 0;
@ -1192,6 +1194,8 @@ int main(int argc, char **argv)
fprintf(iconfig_file, "iwidth:%u\n", integer_width); fprintf(iconfig_file, "iwidth:%u\n", integer_width);
fprintf(iconfig_file, "widthcap:%u\n", width_cap);
/* Write the preprocessor command needed to preprocess a /* Write the preprocessor command needed to preprocess a
single file. This may be used to preprocess library single file. This may be used to preprocess library
files. */ files. */

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1999-2013 Stephen Williams (steve@icarus.com) * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com)
* Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
@ -538,7 +538,7 @@ NetExpr* PEBinary::elaborate_expr_base_mult_(Design*,
return tmp; return tmp;
} }
if (rp_val.is_zero()) { if (rp_val.is_zero() && (lp->expr_type() != IVL_VT_LOGIC)) {
NetEConst*tmp = make_const_0(expr_wid); NetEConst*tmp = make_const_0(expr_wid);
tmp->cast_signed(signed_flag_); tmp->cast_signed(signed_flag_);
tmp->set_line(*this); tmp->set_line(*this);
@ -756,12 +756,11 @@ unsigned PEBLeftWidth::test_width(Design*des, NetScope*scope, width_mode_t&mode)
r_val = rc->value().as_long(); r_val = rc->value().as_long();
// Clip to a sensible range to avoid underflow/overflow // Clip to a sensible range to avoid underflow/overflow
// in the following calculations. 1024 bits should be // in the following calculations.
// enough for anyone...
if (r_val < 0) if (r_val < 0)
r_val = 0; r_val = 0;
if (r_val > 1024) if ((unsigned long)r_val > width_cap)
r_val = 1024; r_val = width_cap;
// If the left operand is a simple unsized number, we // If the left operand is a simple unsized number, we
// can calculate the actual width required for the power // can calculate the actual width required for the power
@ -5373,13 +5372,8 @@ NetExpr* PEUnary::elaborate_expr(Design*des, NetScope*scope,
case '-': case '-':
if (NetEConst*ipc = dynamic_cast<NetEConst*>(ip)) { if (NetEConst*ipc = dynamic_cast<NetEConst*>(ip)) {
verinum val = ipc->value(); verinum val = - ipc->value();
tmp = new NetEConst(val);
/* Calculate unary minus as 0-val */
verinum zero (verinum::V0, expr_wid);
zero.has_sign(val.has_sign());
verinum nval = verinum(zero - val, expr_wid);
tmp = new NetEConst(nval);
tmp->cast_signed(signed_flag_); tmp->cast_signed(signed_flag_);
tmp->set_line(*this); tmp->set_line(*this);
delete ip; delete ip;
@ -5492,7 +5486,7 @@ NetExpr* PEUnary::elaborate_expr_bits_(NetExpr*operand, unsigned expr_wid) const
// The only operand that I know can get here is the // The only operand that I know can get here is the
// unary not (~). // unary not (~).
ivl_assert(*this, op_ == '~'); ivl_assert(*this, op_ == '~');
value = v_not(value); value = ~value;
ctmp = new NetEConst(value); ctmp = new NetEConst(value);
ctmp->set_line(*this); ctmp->set_line(*this);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2000-2013 Stephen Williams (steve@icarus.com) * Copyright (c) 2000-2014 Stephen Williams (steve@icarus.com)
* Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
@ -193,9 +193,7 @@ static void elaborate_scope_enumeration(Design*des, NetScope*scope,
verinum min_value (0); verinum min_value (0);
verinum max_value (0); verinum max_value (0);
if (enum_type->signed_flag) { if (enum_type->signed_flag) {
min_value = v_not((pow(verinum(2), min_value = -pow(verinum(2), verinum(use_enum->packed_width()-1));
verinum(use_enum->packed_width()-1)))) +
one_value;
max_value = pow(verinum(2), verinum(use_enum->packed_width()-1)) - max_value = pow(verinum(2), verinum(use_enum->packed_width()-1)) -
one_value; one_value;
} else { } else {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1998-1999 Stephen Williams (steve@icarus.com) * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -261,7 +261,7 @@ verinum* PEUnary::eval_const(Design*des, NetScope*scope) const
for (unsigned idx = 0 ; idx < val->len() ; idx += 1) for (unsigned idx = 0 ; idx < val->len() ; idx += 1)
tmp.set(idx, val->get(idx)); tmp.set(idx, val->get(idx));
*val = v_not(tmp) + verinum(verinum::V1, 1); *val = -tmp;
val->has_sign(true); val->has_sign(true);
return val; return val;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1999-2013 Stephen Williams (steve@icarus.com) * Copyright (c) 1999-2014 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -175,11 +175,11 @@ NetExpr* NetEBAdd::eval_tree()
if (op_ == se->op_) { if (op_ == se->op_) {
/* (a + lval) + rval --> a + (rval+lval) */ /* (a + lval) + rval --> a + (rval+lval) */
/* (a - lval) - rval --> a - (rval+lval) */ /* (a - lval) - rval --> a - (rval+lval) */
val = verinum(rval + lval, wid); val = cast_to_width(rval + lval, wid);
} else { } else {
/* (a - lval) + rval --> a + (rval-lval) */ /* (a - lval) + rval --> a + (rval-lval) */
/* (a + lval) - rval --> a - (rval-lval) */ /* (a + lval) - rval --> a - (rval-lval) */
val = verinum(rval - lval, wid); val = cast_to_width(rval - lval, wid);
} }
NetEConst*tmp = new NetEConst(val); NetEConst*tmp = new NetEConst(val);
@ -217,10 +217,10 @@ NetExpr* NetEBAdd::eval_arguments_(const NetExpr*l, const NetExpr*r) const
verinum val; verinum val;
switch (op_) { switch (op_) {
case '+': case '+':
val = verinum(lval + rval, wid); val = cast_to_width(lval + rval, wid);
break; break;
case '-': case '-':
val = verinum(lval - rval, wid); val = cast_to_width(lval - rval, wid);
break; break;
default: default:
return 0; return 0;
@ -816,14 +816,15 @@ NetExpr* NetEBDiv::eval_arguments_(const NetExpr*l, const NetExpr*r) const
verinum val; verinum val;
switch (op_) { switch (op_) {
case '/': case '/':
val = verinum(lval / rval, wid); val = cast_to_width(lval / rval, wid);
break; break;
case '%': case '%':
val = verinum(lval % rval, wid); val = cast_to_width(lval % rval, wid);
break; break;
default: default:
return 0; return 0;
} }
NetExpr*tmp = new NetEConst(val); NetExpr*tmp = new NetEConst(val);
ivl_assert(*this, tmp); ivl_assert(*this, tmp);
eval_debug(this, tmp, false); eval_debug(this, tmp, false);
@ -1027,7 +1028,7 @@ NetExpr* NetEBMult::eval_arguments_(const NetExpr*l, const NetExpr*r) const
ivl_assert(*this, lval.len() == wid); ivl_assert(*this, lval.len() == wid);
ivl_assert(*this, rval.len() == wid); ivl_assert(*this, rval.len() == wid);
verinum val(lval * rval, wid); verinum val = cast_to_width(lval * rval, wid);
NetEConst*tmp = new NetEConst(val); NetEConst*tmp = new NetEConst(val);
ivl_assert(*this, tmp); ivl_assert(*this, tmp);
eval_debug(this, tmp, false); eval_debug(this, tmp, false);
@ -1064,7 +1065,7 @@ NetExpr* NetEBPow::eval_arguments_(const NetExpr*l, const NetExpr*r) const
ivl_assert(*this, wid > 0); ivl_assert(*this, wid > 0);
ivl_assert(*this, lval.len() == wid); ivl_assert(*this, lval.len() == wid);
verinum val(pow(lval, rval), wid); verinum val = cast_to_width(pow(lval, rval), wid);
NetEConst*res = new NetEConst(val); NetEConst*res = new NetEConst(val);
ivl_assert(*this, res); ivl_assert(*this, res);
eval_debug(this, res, false); eval_debug(this, res, false);
@ -1088,16 +1089,16 @@ NetEConst* NetEBShift::eval_arguments_(const NetExpr*l, const NetExpr*r) const
verinum val; verinum val;
if (rv.is_defined()) { if (rv.is_defined()) {
unsigned shift = rv.as_ulong(); unsigned shift = rv.as_unsigned();
switch (op_) { switch (op_) {
case 'l': case 'l':
val = verinum(lv << shift, wid); val = cast_to_width(lv << shift, wid);
break; break;
case 'r': case 'r':
lv.has_sign(false); lv.has_sign(false);
case 'R': case 'R':
val = verinum(lv >> shift, wid); val = cast_to_width(lv >> shift, wid);
break; break;
default: default:
return 0; return 0;
@ -1446,14 +1447,7 @@ NetExpr* NetEUnary::eval_arguments_(const NetExpr*ex) const
break; break;
case '-': case '-':
if (val.is_defined()) { val = -val;
verinum tmp (verinum::V0, val.len());
tmp.has_sign(val.has_sign());
val = verinum(tmp - val, val.len());
} else {
for (unsigned idx = 0 ; idx < val.len() ; idx += 1)
val.set(idx, verinum::Vx);
}
break; break;
case 'm': case 'm':
@ -1461,9 +1455,7 @@ NetExpr* NetEUnary::eval_arguments_(const NetExpr*ex) const
for (unsigned idx = 0 ; idx < val.len() ; idx += 1) for (unsigned idx = 0 ; idx < val.len() ; idx += 1)
val.set(idx, verinum::Vx); val.set(idx, verinum::Vx);
} else if (val.is_negative()) { } else if (val.is_negative()) {
verinum tmp (verinum::V0, val.len()); val = -val;
tmp.has_sign(val.has_sign());
val = verinum(tmp - val, val.len());
} }
break; break;

10
main.cc
View File

@ -1,5 +1,5 @@
const char COPYRIGHT[] = const char COPYRIGHT[] =
"Copyright (c) 1998-2013 Stephen Williams (steve@icarus.com)"; "Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com)";
/* /*
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
@ -191,6 +191,11 @@ bool verbose_flag = false;
unsigned integer_width = 32; unsigned integer_width = 32;
/*
* Width limit for unsized expressions.
*/
unsigned width_cap = 65536;
int def_ts_units = 0; int def_ts_units = 0;
int def_ts_prec = 0; int def_ts_prec = 0;
@ -647,6 +652,9 @@ static void read_iconfig_file(const char*ipath)
} else if (strcmp(buf, "iwidth") == 0) { } else if (strcmp(buf, "iwidth") == 0) {
integer_width = strtoul(cp,0,10); integer_width = strtoul(cp,0,10);
} else if (strcmp(buf, "widthcap") == 0) {
width_cap = strtoul(cp,0,10);
} else if (strcmp(buf, "library_file") == 0) { } else if (strcmp(buf, "library_file") == 0) {
perm_string path = filename_strings.make(cp); perm_string path = filename_strings.make(cp);
library_file_map[path] = true; library_file_map[path] = true;

View File

@ -761,9 +761,9 @@ static NetExpr* do_elab_and_eval(Design*des, NetScope*scope, PExpr*pe,
// If context_width is positive, this is the RHS of an assignment, // If context_width is positive, this is the RHS of an assignment,
// so the LHS width must also be included in the width calculation. // so the LHS width must also be included in the width calculation.
if ((context_width > 0) && (pe->expr_type() != IVL_VT_REAL) unsigned pos_context_width = context_width > 0 ? context_width : 0;
&& (expr_width < (unsigned)context_width)) if ((pe->expr_type() != IVL_VT_REAL) && (expr_width < pos_context_width))
expr_width = context_width; expr_width = pos_context_width;
if (debug_elaborate) { if (debug_elaborate) {
cerr << pe->get_fileline() << ": elab_and_eval: test_width of " cerr << pe->get_fileline() << ": elab_and_eval: test_width of "
@ -783,8 +783,8 @@ static NetExpr* do_elab_and_eval(Design*des, NetScope*scope, PExpr*pe,
// width, do so. // width, do so.
if ((context_width > 0) && (!force_expand) if ((context_width > 0) && (!force_expand)
&& (pe->expr_type() != IVL_VT_REAL) && (pe->expr_type() != IVL_VT_REAL)
&& (expr_width > (unsigned)context_width)) { && (expr_width > pos_context_width)) {
expr_width = max(pe->min_width(), (unsigned)context_width); expr_width = max(pe->min_width(), pos_context_width);
if (debug_elaborate) { if (debug_elaborate) {
cerr << pe->get_fileline() << ": " cerr << pe->get_fileline() << ": "
@ -792,6 +792,15 @@ static NetExpr* do_elab_and_eval(Design*des, NetScope*scope, PExpr*pe,
} }
} }
if ((mode >= PExpr::LOSSLESS) && (expr_width > width_cap)
&& (expr_width > pos_context_width)) {
cerr << pe->get_fileline() << ": warning: excessive unsized "
<< "expression width detected." << endl;
cerr << pe->get_fileline() << ": : The expression width "
<< "is capped at " << width_cap << " bits." << endl;
expr_width = width_cap;
}
unsigned flags = PExpr::NO_FLAGS; unsigned flags = PExpr::NO_FLAGS;
if (need_const) if (need_const)
flags |= PExpr::NEED_CONST; flags |= PExpr::NEED_CONST;
@ -812,10 +821,10 @@ static NetExpr* do_elab_and_eval(Design*des, NetScope*scope, PExpr*pe,
tmp = cast_to_real(tmp); tmp = cast_to_real(tmp);
break; break;
case IVL_VT_BOOL: case IVL_VT_BOOL:
tmp = cast_to_int2(tmp, context_width > 0 ? context_width : 0); tmp = cast_to_int2(tmp, pos_context_width);
break; break;
case IVL_VT_LOGIC: case IVL_VT_LOGIC:
tmp = cast_to_int4(tmp, context_width > 0 ? context_width : 0); tmp = cast_to_int4(tmp, pos_context_width);
break; break;
default: default:
break; break;

View File

@ -1,7 +1,7 @@
%{ %{
/* /*
* Copyright (c) 1998-2013 Stephen Williams (steve@icarus.com) * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com)
* Copyright CERN 2012-2013 / Stephen Williams (steve@icarus.com) * Copyright CERN 2012-2013 / Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
@ -2259,7 +2259,7 @@ pos_neg_number
{ $$ = $1; { $$ = $1;
} }
| '-' number | '-' number
{ verinum tmp = v_not(*($2)) + verinum(1); { verinum tmp = -(*($2));
*($2) = tmp; *($2) = tmp;
$$ = $2; $$ = $2;
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2001-2013 Stephen Williams (steve@icarus.com) * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -1080,6 +1080,7 @@ static struct vector_info draw_binary_expr_le(ivl_expr_t expr,
return lv; return lv;
} }
static struct vector_info draw_binary_expr(ivl_expr_t expr, static struct vector_info draw_binary_expr(ivl_expr_t expr,
unsigned wid, unsigned wid,
int stuff_ok_flag) int stuff_ok_flag)

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1998-2013 Stephen Williams (steve@icarus.com) * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -24,6 +24,7 @@
# include <cassert> # include <cassert>
# include <cmath> // Needed to get pow for as_double(). # include <cmath> // Needed to get pow for as_double().
# include <cstdio> // Needed to get snprintf for as_string(). # include <cstdio> // Needed to get snprintf for as_string().
# include <algorithm>
#if !defined(HAVE_LROUND) #if !defined(HAVE_LROUND)
/* /*
@ -200,7 +201,6 @@ verinum::verinum(double val, bool)
fraction = frexp(val, &exponent); fraction = frexp(val, &exponent);
nbits_ = exponent+1; nbits_ = exponent+1;
bits_ = new V[nbits_]; bits_ = new V[nbits_];
const verinum const_one(1);
/* If the value is small enough just use lround(). */ /* If the value is small enough just use lround(). */
if (nbits_ <= BITS_IN_LONG) { if (nbits_ <= BITS_IN_LONG) {
@ -230,9 +230,9 @@ verinum::verinum(double val, bool)
for (int wd = nwords; wd >= 0; wd -= 1) { for (int wd = nwords; wd >= 0; wd -= 1) {
unsigned long bits = (unsigned long) fraction; unsigned long bits = (unsigned long) fraction;
fraction = fraction - (double) bits; fraction = fraction - (double) bits;
unsigned max = (wd+1)*BITS_IN_LONG; unsigned max_idx = (wd+1)*BITS_IN_LONG;
if (max > nbits_) max = nbits_; if (max_idx > nbits_) max_idx = nbits_;
for (unsigned idx = wd*BITS_IN_LONG; idx < max; idx += 1) { for (unsigned idx = wd*BITS_IN_LONG; idx < max_idx; idx += 1) {
bits_[idx] = (bits&1) ? V1 : V0; bits_[idx] = (bits&1) ? V1 : V0;
bits >>= 1; bits >>= 1;
} }
@ -242,7 +242,7 @@ verinum::verinum(double val, bool)
/* Convert a negative number if needed. */ /* Convert a negative number if needed. */
if (is_neg) { if (is_neg) {
*this = v_not(*this) + const_one; *this = -(*this);
} }
/* Trim the result. */ /* Trim the result. */
@ -378,6 +378,25 @@ void verinum::set(unsigned off, const verinum&val)
bits_[off+idx] = val[idx]; bits_[off+idx] = val[idx];
} }
unsigned verinum::as_unsigned() const
{
if (nbits_ == 0)
return 0;
if (!is_defined())
return 0;
unsigned val = 0;
unsigned mask = 1;
for (unsigned idx = 0 ; idx < nbits_ ; idx += 1, mask <<= 1)
if (bits_[idx] == V1) {
if (mask == 0) return ~mask;
val |= mask;
}
return val;
}
unsigned long verinum::as_ulong() const unsigned long verinum::as_ulong() const
{ {
if (nbits_ == 0) if (nbits_ == 0)
@ -386,15 +405,13 @@ unsigned long verinum::as_ulong() const
if (!is_defined()) if (!is_defined())
return 0; return 0;
unsigned top = nbits_;
if (top >= (8 * sizeof(unsigned long)))
top = 8 * sizeof(unsigned long);
unsigned long val = 0; unsigned long val = 0;
unsigned long mask = 1; unsigned long mask = 1;
for (unsigned idx = 0 ; idx < top ; idx += 1, mask <<= 1) for (unsigned idx = 0 ; idx < nbits_ ; idx += 1, mask <<= 1)
if (bits_[idx] == V1) if (bits_[idx] == V1) {
if (mask == 0) return ~mask;
val |= mask; val |= mask;
}
return val; return val;
} }
@ -407,15 +424,13 @@ uint64_t verinum::as_ulong64() const
if (!is_defined()) if (!is_defined())
return 0; return 0;
unsigned top = nbits_;
if (top >= (8 * sizeof(uint64_t)))
top = 8 * sizeof(uint64_t);
uint64_t val = 0; uint64_t val = 0;
uint64_t mask = 1; uint64_t mask = 1;
for (unsigned idx = 0 ; idx < top ; idx += 1, mask <<= 1) for (unsigned idx = 0 ; idx < nbits_ ; idx += 1, mask <<= 1)
if (bits_[idx] == V1) if (bits_[idx] == V1) {
if (mask == 0) return ~mask;
val |= mask; val |= mask;
}
return val; return val;
} }
@ -968,7 +983,7 @@ static verinum::V add_with_carry(verinum::V l, verinum::V r, verinum::V&c)
return verinum::V0; return verinum::V0;
} }
verinum v_not(const verinum&left) verinum operator ~ (const verinum&left)
{ {
verinum val = left; verinum val = left;
for (unsigned idx = 0 ; idx < val.len() ; idx += 1) for (unsigned idx = 0 ; idx < val.len() ; idx += 1)
@ -988,137 +1003,190 @@ verinum v_not(const verinum&left)
} }
/* /*
* Addition works a bit at a time, from the least significant up to * Addition and subtraction works a bit at a time, from the least
* the most significant. The result is signed only if both of the * significant up to the most significant. The result is signed only
* operands are signed. The result is also expanded as needed to * if both of the operands are signed. If either operand is unsized,
* prevent overflow. It is up to the caller to shrink the result back * the result is expanded as needed to prevent overflow.
* down if that is the desire.
*/ */
verinum operator + (const verinum&left, const verinum&right) verinum operator + (const verinum&left, const verinum&right)
{ {
unsigned min = left.len(); const bool has_len_flag = left.has_len() && right.has_len();
if (right.len() < min) min = right.len(); const bool signed_flag = left.has_sign() && right.has_sign();
unsigned max = left.len(); unsigned min_len = min(left.len(), right.len());
if (right.len() > max) max = right.len(); unsigned max_len = max(left.len(), right.len());
bool signed_flag = left.has_sign() && right.has_sign(); // If either the left or right values are undefined, the
verinum::V*val_bits = new verinum::V[max+1]; // entire result is undefined.
if (!left.is_defined() || !right.is_defined()) {
unsigned len = has_len_flag ? max_len : 1;
verinum result (verinum::Vx, len, has_len_flag);
result.has_sign(signed_flag);
return result;
}
verinum::V*val_bits = new verinum::V[max_len+1];
verinum::V carry = verinum::V0; verinum::V carry = verinum::V0;
for (unsigned idx = 0 ; idx < min ; idx += 1) for (unsigned idx = 0 ; idx < min_len ; idx += 1)
val_bits[idx] = add_with_carry(left[idx], right[idx], carry); val_bits[idx] = add_with_carry(left[idx], right[idx], carry);
verinum::V rpad = signed_flag? right[right.len()-1] : verinum::V0; verinum::V rpad = sign_bit(right);
verinum::V lpad = signed_flag? left[left.len()-1] : verinum::V0; verinum::V lpad = sign_bit(left);
if (left.len() > right.len()) { if (left.len() > right.len()) {
for (unsigned idx = min ; idx < left.len() ; idx += 1) for (unsigned idx = min_len ; idx < max_len ; idx += 1)
val_bits[idx] = add_with_carry(left[idx], rpad, carry); val_bits[idx] = add_with_carry(left[idx], rpad, carry);
} else { } else {
for (unsigned idx = min ; idx < right.len() ; idx += 1) for (unsigned idx = min_len ; idx < max_len ; idx += 1)
val_bits[idx] = add_with_carry(lpad, right[idx], carry); val_bits[idx] = add_with_carry(lpad, right[idx], carry);
} }
val_bits[max] = add_with_carry(lpad, rpad, carry); unsigned len = max_len;
#if 0 if (!has_len_flag) {
if (signed_flag) { val_bits[max_len] = add_with_carry(lpad, rpad, carry);
if (val_bits[max] != val_bits[max-1]) if (signed_flag) {
max += 1; if (val_bits[max_len] != val_bits[max_len-1]) len += 1;
} else {
if (val_bits[max_len] != verinum::V0) len += 1;
}
} }
#endif verinum result (val_bits, len, has_len_flag);
verinum val (val_bits, max+1, false); result.has_sign(signed_flag);
val.has_sign(signed_flag);
delete[]val_bits; delete[]val_bits;
return val; return result;
} }
verinum operator - (const verinum&left, const verinum&right) verinum operator - (const verinum&left, const verinum&right)
{ {
unsigned min = left.len(); const bool has_len_flag = left.has_len() && right.has_len();
if (right.len() < min) min = right.len(); const bool signed_flag = left.has_sign() && right.has_sign();
unsigned max = left.len(); unsigned min_len = min(left.len(), right.len());
if (right.len() > max) max = right.len(); unsigned max_len = max(left.len(), right.len());
bool signed_flag = left.has_sign() && right.has_sign(); // If either the left or right values are undefined, the
verinum::V*val_bits = new verinum::V[max+1]; // entire result is undefined.
if (!left.is_defined() || !right.is_defined()) {
unsigned len = has_len_flag ? max_len : 1;
verinum result (verinum::Vx, len, has_len_flag);
result.has_sign(signed_flag);
return result;
}
verinum::V*val_bits = new verinum::V[max_len+1];
verinum::V carry = verinum::V1; verinum::V carry = verinum::V1;
for (unsigned idx = 0 ; idx < min ; idx += 1) for (unsigned idx = 0 ; idx < min_len ; idx += 1)
val_bits[idx] = add_with_carry(left[idx], ~right[idx], carry); val_bits[idx] = add_with_carry(left[idx], ~right[idx], carry);
verinum::V rpad = signed_flag? ~right[right.len()-1] : verinum::V1; verinum::V rpad = sign_bit(right);
verinum::V lpad = signed_flag? left[left.len()-1] : verinum::V0; verinum::V lpad = sign_bit(left);
if (left.len() > right.len()) { if (left.len() > right.len()) {
for (unsigned idx = min ; idx < left.len() ; idx += 1) for (unsigned idx = min_len ; idx < max_len ; idx += 1)
val_bits[idx] = add_with_carry(left[idx], rpad, carry); val_bits[idx] = add_with_carry(left[idx], ~rpad, carry);
} else { } else {
for (unsigned idx = min ; idx < right.len() ; idx += 1) for (unsigned idx = min_len ; idx < max_len ; idx += 1)
val_bits[idx] = add_with_carry(lpad, ~right[idx], carry); val_bits[idx] = add_with_carry(lpad, ~right[idx], carry);
} }
if (signed_flag) { unsigned len = max_len;
val_bits[max] = add_with_carry(lpad, rpad, carry); if (signed_flag && !has_len_flag) {
if (val_bits[max] != val_bits[max-1]) val_bits[max_len] = add_with_carry(lpad, ~rpad, carry);
max += 1; if (val_bits[max_len] != val_bits[max_len-1]) len += 1;
} }
verinum result (val_bits, len, has_len_flag);
verinum val (val_bits, max, false); result.has_sign(signed_flag);
val.has_sign(signed_flag);
delete[]val_bits; delete[]val_bits;
return val; return result;
}
verinum operator - (const verinum&right)
{
const bool has_len_flag = right.has_len();
const bool signed_flag = right.has_sign();
unsigned len = right.len();
// If either the left or right values are undefined, the
// entire result is undefined.
if (!right.is_defined()) {
verinum result (verinum::Vx, has_len_flag ? len : 1, has_len_flag);
result.has_sign(signed_flag);
return result;
}
verinum::V*val_bits = new verinum::V[len+1];
verinum::V carry = verinum::V1;
for (unsigned idx = 0 ; idx < len ; idx += 1)
val_bits[idx] = add_with_carry(verinum::V0, ~right[idx], carry);
if (signed_flag && !has_len_flag) {
val_bits[len] = add_with_carry(verinum::V0, ~sign_bit(right), carry);
if (val_bits[len] != val_bits[len-1]) len += 1;
}
verinum result (val_bits, len, has_len_flag);
result.has_sign(signed_flag);
delete[]val_bits;
return result;
} }
/* /*
* This multiplies two verinum numbers together into a verinum * This operator multiplies the left number by the right number. The
* result. The resulting number is as large as the sum of the sizes of * result is signed only if both of the operands are signed. If either
* the operand. * operand is unsized, the resulting number is as large as the sum of
* the sizes of the operands.
* *
* The algorithm used is successive shift and add operations, * The algorithm used is successive shift and add operations,
* implemented as the nested loops. * implemented as the nested loops.
*
* If either value is not completely defined, then the result is not
* defined either.
*/ */
verinum operator * (const verinum&left, const verinum&right) verinum operator * (const verinum&left, const verinum&right)
{ {
const bool has_len_flag = left.has_len() && right.has_len(); const bool has_len_flag = left.has_len() && right.has_len();
const bool signed_flag = left.has_sign() && right.has_sign();
/* If either operand is not fully defined, then the entire const unsigned l_len = left.len();
result is undefined. Create a result that is the right size const unsigned r_len = right.len();
and is filled with 'bx bits. */
if (! (left.is_defined() && right.is_defined())) { unsigned len = has_len_flag ? max(l_len, r_len) : l_len + r_len;
verinum result (verinum::Vx, left.len()+right.len(), has_len_flag);
result.has_sign(left.has_sign() || right.has_sign()); // If either the left or right values are undefined, the
// entire result is undefined.
if (!left.is_defined() || !right.is_defined()) {
verinum result (verinum::Vx, has_len_flag ? len : 1, has_len_flag);
result.has_sign(signed_flag);
return result; return result;
} }
verinum result(verinum::V0, left.len() + right.len(), has_len_flag); verinum result(verinum::V0, len, has_len_flag);
result.has_sign(left.has_sign() || right.has_sign()); result.has_sign(signed_flag);
verinum::V r_sign = sign_bit(right); verinum::V r_sign = sign_bit(right);
for (unsigned rdx = 0 ; rdx < result.len() ; rdx += 1) { for (unsigned rdx = 0 ; rdx < len ; rdx += 1) {
verinum::V r_bit = rdx < right.len()? right.get(rdx) : r_sign; verinum::V r_bit = rdx < r_len ? right.get(rdx) : r_sign;
if (r_bit == verinum::V0) if (r_bit == verinum::V0)
continue; continue;
verinum::V l_sign = sign_bit(left); verinum::V l_sign = sign_bit(left);
verinum::V carry = verinum::V0; verinum::V carry = verinum::V0;
for (unsigned ldx = 0 ; ldx < result.len()-rdx ; ldx += 1) { for (unsigned ldx = 0 ; ldx < (len - rdx) ; ldx += 1) {
verinum::V l_bit = ldx < left.len()? left[ldx] : l_sign; verinum::V l_bit = ldx < l_len ? left[ldx] : l_sign;
result.set(ldx+rdx, add_with_carry(l_bit, result.set(ldx+rdx, add_with_carry(l_bit,
result[rdx+ldx], result[rdx+ldx],
carry)); carry));
@ -1150,22 +1218,20 @@ static verinum recursive_pow(const verinum&left, verinum&right)
return make_p_one(left.len(), left.has_len(), left.has_sign()); return make_p_one(left.len(), left.has_len(), left.has_sign());
} }
verinum res; verinum result;
if (right.get(0) == 1) { if (right.get(0) == 1) {
// The exponent is odd, so subtract 1 from it and recurse // The exponent is odd, so subtract 1 from it and recurse.
// We know it's odd, so the subtraction is easy.
right.set(0, verinum::V0); right.set(0, verinum::V0);
res = pow(left, right); result = pow(left, right);
res = left * res; result = left * result;
} else { } else {
// The exponent is even, so divide it by 2 and recurse // The exponent is even, so divide it by 2 and recurse
right = right >> 1; right = right >> 1;
res = pow(left, right); result = pow(left, right);
res = res * res; result = result * result;
} }
if (left.has_len()) { return result;
res = verinum(res, left.len());
}
return res;
} }
verinum pow(const verinum&left, const verinum&right) verinum pow(const verinum&left, const verinum&right)
@ -1176,8 +1242,9 @@ verinum pow(const verinum&left, const verinum&right)
verinum p_one = make_p_one(left.len(), left.has_len(), left.has_sign()); verinum p_one = make_p_one(left.len(), left.has_len(), left.has_sign());
verinum m_one = make_m_one(left.len(), left.has_len(), left.has_sign()); verinum m_one = make_m_one(left.len(), left.has_len(), left.has_sign());
// If either the right or left values are undefined we return 'bx. // If either the left or right values are undefined, the
if (!right.is_defined() || !left.is_defined()) { // entire result is undefined.
if (!left.is_defined() || !right.is_defined()) {
result = verinum(verinum::Vx, left.len(), left.has_len()); result = verinum(verinum::Vx, left.len(), left.has_len());
result.has_sign(left.has_sign()); result.has_sign(left.has_sign());
@ -1221,40 +1288,52 @@ verinum pow(const verinum&left, const verinum&right)
verinum operator << (const verinum&that, unsigned shift) verinum operator << (const verinum&that, unsigned shift)
{ {
verinum result(verinum::V0, that.len() + shift, that.has_len()); bool has_len_flag = that.has_len();
unsigned len = that.len();
if (!has_len_flag) len += shift;
verinum result(verinum::V0, len, has_len_flag);
result.has_sign(that.has_sign()); result.has_sign(that.has_sign());
for (unsigned idx = 0 ; idx < that.len() ; idx += 1) for (unsigned idx = shift ; idx < len ; idx += 1)
result.set(idx+shift, that.get(idx)); result.set(idx, that.get(idx - shift));
return result; return trim_vnum(result);
} }
verinum operator >> (const verinum&that, unsigned shift) verinum operator >> (const verinum&that, unsigned shift)
{ {
if (shift >= that.len()) { bool has_len_flag = that.has_len();
if (that.has_sign()) {
verinum result (that.get(that.len()-1), 1); unsigned len = that.len();
result.has_sign(true);
return result; verinum::V sign_bit = that.has_sign() ? that.get(len-1) : verinum::V0;
} else {
verinum result(verinum::V0, 1); if (shift >= len) {
return result; if (!has_len_flag) len = 1;
} verinum result(sign_bit, len, has_len_flag);
result.has_sign(that.has_sign());
return result;
} }
verinum result(that.has_sign()? that.get(that.len()-1) : verinum::V0, if (!has_len_flag) len -= shift;
that.len() - shift, that.has_len()); verinum result(sign_bit, len, has_len_flag);
result.has_sign(that.has_sign()); result.has_sign(that.has_sign());
for (unsigned idx = shift ; idx < that.len() ; idx += 1) for (unsigned idx = shift ; idx < that.len() ; idx += 1)
result.set(idx-shift, that.get(idx)); result.set(idx-shift, that.get(idx));
return result; return trim_vnum(result);
} }
static verinum unsigned_divide(verinum num, verinum den, bool signed_result) static verinum unsigned_divide(verinum num, verinum den, bool signed_result)
{ {
// We need the following calculations to be lossless. The
// result will be cast to the required width by the caller.
num.has_len(false);
den.has_len(false);
unsigned nwid = num.len(); unsigned nwid = num.len();
while (nwid > 0 && (num.get(nwid-1) == verinum::V0)) while (nwid > 0 && (num.get(nwid-1) == verinum::V0))
nwid -= 1; nwid -= 1;
@ -1289,6 +1368,11 @@ static verinum unsigned_divide(verinum num, verinum den, bool signed_result)
static verinum unsigned_modulus(verinum num, verinum den) static verinum unsigned_modulus(verinum num, verinum den)
{ {
// We need the following calculations to be lossless. The
// result will be cast to the required width by the caller.
num.has_len(false);
den.has_len(false);
unsigned nwid = num.len(); unsigned nwid = num.len();
while (nwid > 0 && (num.get(nwid-1) == verinum::V0)) while (nwid > 0 && (num.get(nwid-1) == verinum::V0))
nwid -= 1; nwid -= 1;
@ -1316,39 +1400,29 @@ static verinum unsigned_modulus(verinum num, verinum den)
} }
/* /*
* This operator divides the left number by the right number. If * This operator divides the left number by the right number. The result
* either value is signed, the result is signed. If both values have a * is signed only if both of the operands are signed.
* defined length, then the result has a defined length.
*/ */
verinum operator / (const verinum&left, const verinum&right) verinum operator / (const verinum&left, const verinum&right)
{ {
const bool has_len_flag = left.has_len() && right.has_len(); const bool has_len_flag = left.has_len() && right.has_len();
const bool signed_flag = left.has_sign() && right.has_sign();
unsigned use_len = left.len(); unsigned use_len = left.len();
/* If either operand is not fully defined, then the entire // If either the left or right values are undefined, or the
result is undefined. Create a result that is the right size // right value is zero, the entire result is undefined.
and is filled with 'bx bits. */ if (!left.is_defined() || !right.is_defined() || right.is_zero()) {
if (! (left.is_defined() && right.is_defined())) {
verinum result (verinum::Vx, use_len, has_len_flag); verinum result (verinum::Vx, use_len, has_len_flag);
result.has_sign(left.has_sign() || right.has_sign()); result.has_sign(signed_flag);
return result;
}
/* If the right expression is a zero value, then the result is
filled with 'bx bits. */
if (right.is_zero()) {
verinum result (verinum::Vx, use_len, has_len_flag);
result.has_sign(left.has_sign() || right.has_sign());
return result; return result;
} }
verinum result(verinum::Vz, use_len, has_len_flag); verinum result(verinum::Vz, use_len, has_len_flag);
result.has_sign(left.has_sign() || right.has_sign());
/* do the operation differently, depending on whether the /* do the operation differently, depending on whether the
result is signed or not. */ result is signed or not. */
if (result.has_sign()) { if (signed_flag) {
if (use_len <= (8*sizeof(long) - 1)) { if (use_len <= (8*sizeof(long) - 1)) {
long l = left.as_long(); long l = left.as_long();
@ -1361,23 +1435,23 @@ verinum operator / (const verinum&left, const verinum&right)
} else { } else {
verinum use_left, use_right; verinum use_left, use_right;
verinum zero(verinum::V0, 1, false);
zero.has_sign(true);
bool negative = false; bool negative = false;
if (left < zero) { if (left.is_negative()) {
use_left = zero - left; use_left = -left;
negative = !negative; negative = !negative;
} else { } else {
use_left = left; use_left = left;
} }
if (right < zero) { use_left.has_sign(false);
use_right = zero - right; if (right.is_negative()) {
use_right = -right;
negative = !negative; negative = !negative;
} else { } else {
use_right = right; use_right = right;
} }
use_right.has_sign(false);
result = unsigned_divide(use_left, use_right, true); result = unsigned_divide(use_left, use_right, true);
if (negative) result = zero - result; if (negative) result = -result;
} }
} else { } else {
@ -1398,36 +1472,31 @@ verinum operator / (const verinum&left, const verinum&right)
} }
} }
if (has_len_flag)
result = cast_to_width(result, use_len);
result.has_sign(signed_flag);
return trim_vnum(result); return trim_vnum(result);
} }
verinum operator % (const verinum&left, const verinum&right) verinum operator % (const verinum&left, const verinum&right)
{ {
const bool has_len_flag = left.has_len() && right.has_len(); const bool has_len_flag = left.has_len() && right.has_len();
const bool signed_flag = left.has_sign() && right.has_sign();
unsigned use_len = left.len(); unsigned use_len = left.len();
/* If either operand is not fully defined, then the entire // If either the left or right values are undefined, or the
result is undefined. Create a result that is the right size // right value is zero, the entire result is undefined.
and is filled with 'bx bits. */ if (!left.is_defined() || !right.is_defined() || right.is_zero()) {
if (! (left.is_defined() && right.is_defined())) {
verinum result (verinum::Vx, use_len, has_len_flag); verinum result (verinum::Vx, use_len, has_len_flag);
result.has_sign(left.has_sign() || right.has_sign()); result.has_sign(signed_flag);
return result;
}
/* If the right expression is a zero value, then the result is
filled with 'bx bits. */
if (right.as_ulong() == 0) {
verinum result (verinum::Vx, use_len, has_len_flag);
result.has_sign(left.has_sign() || right.has_sign());
return result; return result;
} }
verinum result(verinum::Vz, use_len, has_len_flag); verinum result(verinum::Vz, use_len, has_len_flag);
result.has_sign(left.has_sign() || right.has_sign());
if (result.has_sign()) { if (signed_flag) {
if (use_len <= 8*sizeof(long)) { if (use_len <= 8*sizeof(long)) {
/* Use native signed modulus to do the work. */ /* Use native signed modulus to do the work. */
long l = left.as_long(); long l = left.as_long();
@ -1439,23 +1508,22 @@ verinum operator % (const verinum&left, const verinum&right)
} }
} else { } else {
verinum use_left, use_right; verinum use_left, use_right;
verinum zero(verinum::V0, 1, false);
zero.has_sign(true);
bool negative = false; bool negative = false;
if (left < zero) { if (left.is_negative()) {
use_left = zero - left; use_left = -left;
negative = true; negative = true;
} else { } else {
use_left = left; use_left = left;
} }
if (right < zero) { use_left.has_sign(false);
use_right = zero - right; if (right.is_negative()) {
use_right = -right;
} else { } else {
use_right = right; use_right = right;
} }
use_right.has_sign(false);
result = unsigned_modulus(use_left, use_right); result = unsigned_modulus(use_left, use_right);
result.has_sign(true); if (negative) result = -result;
if (negative) result = zero - result;
} }
} else { } else {
if (use_len <= 8*sizeof(unsigned long)) { if (use_len <= 8*sizeof(unsigned long)) {
@ -1472,6 +1540,10 @@ verinum operator % (const verinum&left, const verinum&right)
} }
} }
if (has_len_flag)
result = cast_to_width(result, use_len);
result.has_sign(signed_flag);
return trim_vnum(result); return trim_vnum(result);
} }

View File

@ -1,7 +1,7 @@
#ifndef __verinum_H #ifndef __verinum_H
#define __verinum_H #define __verinum_H
/* /*
* Copyright (c) 1998-2013 Stephen Williams (steve@icarus.com) * Copyright (c) 1998-2014 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -97,9 +97,14 @@ class verinum {
V operator[] (unsigned idx) const { return get(idx); } V operator[] (unsigned idx) const { return get(idx); }
// Return the value as a native unsigned integer. If the value is
// larger than can be represented by the returned type, return
// the maximum value of that type. If the value has any x or z
// bits or has zero width, return the value 0.
uint64_t as_ulong64() const; uint64_t as_ulong64() const;
unsigned as_unsigned() const;
unsigned long as_ulong() const; unsigned long as_ulong() const;
signed long as_long() const; signed long as_long() const;
double as_double() const; double as_double() const;
string as_string() const; string as_string() const;
@ -171,11 +176,13 @@ inline verinum::V operator != (const verinum&left, const verinum&right)
{ return (left == right)? verinum::V0 : verinum::V1; } { return (left == right)? verinum::V0 : verinum::V1; }
/* These are arithmetic operators. These generally work to produce /* These are arithmetic operators. If any operand is unsized, they
results that do not overflow. That means the result may expand or generally work to produce results that do not overflow. That means
contract to hold the bits needed to hold the operation results the result may expand or contract to hold the bits needed to hold
accurately. It is up to the caller to truncate or pad if a specific the operation results accurately. It is up to the caller to truncate
width is expected. */ or pad if a specific width is expected. If all operands are sized,
the normal Verilog rules for result size are used. */
extern verinum operator - (const verinum&right);
extern verinum operator + (const verinum&left, const verinum&right); extern verinum operator + (const verinum&left, const verinum&right);
extern verinum operator - (const verinum&left, const verinum&right); extern verinum operator - (const verinum&left, const verinum&right);
extern verinum operator * (const verinum&left, const verinum&right); extern verinum operator * (const verinum&left, const verinum&right);
@ -190,6 +197,6 @@ extern verinum operator>> (const verinum&left, unsigned shift);
extern verinum concat(const verinum&left, const verinum&right); extern verinum concat(const verinum&left, const verinum&right);
/* Bitwise not returns the ones complement. */ /* Bitwise not returns the ones complement. */
extern verinum v_not(const verinum&left); extern verinum operator ~ (const verinum&left);
#endif #endif

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2001-2013 Stephen Williams (steve@icarus.com) * Copyright (c) 2001-2014 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -1768,7 +1768,7 @@ bool of_BREAKPOINT(vthread_t, vvp_code_t)
} }
/* /*
* the %cassign/link instruction connects a source node to a * The %cassign/link instruction connects a source node to a
* destination node. The destination node must be a signal, as it is * destination node. The destination node must be a signal, as it is
* marked with the source of the cassign so that it may later be * marked with the source of the cassign so that it may later be
* unlinked without specifically knowing the source that this * unlinked without specifically knowing the source that this
@ -1783,17 +1783,8 @@ bool of_CASSIGN_LINK(vthread_t, vvp_code_t cp)
= dynamic_cast<vvp_fun_signal_base*>(dst->fun); = dynamic_cast<vvp_fun_signal_base*>(dst->fun);
assert(sig); assert(sig);
/* Detect the special case that we are already continuous /* Any previous continuous assign should have been removed already. */
assigning the source onto the destination. */ assert(sig->cassign_link == 0);
if (sig->cassign_link == src)
return true;
/* If there is an existing cassign driving this node, then
unlink it. We can have only 1 cassign at a time. */
if (sig->cassign_link != 0) {
vvp_net_ptr_t tmp (dst, 1);
sig->cassign_link->unlink(tmp);
}
sig->cassign_link = src; sig->cassign_link = src;
@ -1806,7 +1797,27 @@ bool of_CASSIGN_LINK(vthread_t, vvp_code_t cp)
} }
/* /*
* the %cassign/vec4 instruction invokes a continuous assign of a * If there is an existing continuous assign linked to the destination
* node, unlink it. This must be done before applying a new continuous
* assign, otherwise the initial assigned value will be propagated to
* any other nodes driven by the old continuous assign source.
*/
static void cassign_unlink(vvp_net_t*dst)
{
vvp_fun_signal_base*sig
= dynamic_cast<vvp_fun_signal_base*>(dst->fun);
assert(sig);
if (sig->cassign_link == 0)
return;
vvp_net_ptr_t tmp (dst, 1);
sig->cassign_link->unlink(tmp);
sig->cassign_link = 0;
}
/*
* The %cassign/v instruction invokes a continuous assign of a
* constant value to a signal. The instruction arguments are: * constant value to a signal. The instruction arguments are:
* *
* %cassign/vec4 <net>; * %cassign/vec4 <net>;
@ -1822,7 +1833,10 @@ bool of_CASSIGN_VEC4(vthread_t thr, vvp_code_t cp)
vvp_net_t*net = cp->net; vvp_net_t*net = cp->net;
vvp_vector4_t value = thr->pop_vec4(); vvp_vector4_t value = thr->pop_vec4();
/* set the value into port 1 of the destination. */ /* Remove any previous continuous assign to this net. */
cassign_unlink(net);
/* Set the value into port 1 of the destination. */
vvp_net_ptr_t ptr (net, 1); vvp_net_ptr_t ptr (net, 1);
vvp_send_vec4(ptr, value, 0); vvp_send_vec4(ptr, value, 0);
@ -1843,6 +1857,9 @@ bool of_CASSIGN_VEC4_OFF(vthread_t thr, vvp_code_t cp)
if (thr->flags[4] == BIT4_1) if (thr->flags[4] == BIT4_1)
return true; return true;
/* Remove any previous continuous assign to this net. */
cassign_unlink(net);
vvp_signal_value*sig = dynamic_cast<vvp_signal_value*> (net->fil); vvp_signal_value*sig = dynamic_cast<vvp_signal_value*> (net->fil);
assert(sig); assert(sig);
@ -1873,6 +1890,9 @@ bool of_CASSIGN_WR(vthread_t thr, vvp_code_t cp)
vvp_net_t*net = cp->net; vvp_net_t*net = cp->net;
double value = thr->pop_real(); double value = thr->pop_real();
/* Remove any previous continuous assign to this net. */
cassign_unlink(net);
/* Set the value into port 1 of the destination. */ /* Set the value into port 1 of the destination. */
vvp_net_ptr_t ptr (net, 1); vvp_net_ptr_t ptr (net, 1);
vvp_send_real(ptr, value, 0); vvp_send_real(ptr, value, 0);

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2004-2013 Stephen Williams (steve@icarus.com) * Copyright (c) 2004-2014 Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
* and/or modify it in source code form under the terms of the GNU * and/or modify it in source code form under the terms of the GNU
@ -2364,7 +2364,7 @@ vvp_vector2_t pow(const vvp_vector2_t&x, vvp_vector2_t&y)
{ {
/* If we have a zero exponent just return 1. */ /* If we have a zero exponent just return 1. */
if (y == vvp_vector2_t(0L, 1)) { if (y == vvp_vector2_t(0L, 1)) {
return vvp_vector2_t(1L, x.size()); return vvp_vector2_t(1L, x.size());
} }
/* Is the value odd? */ /* Is the value odd? */