Add support for the array querying function in a constant context

This patchs adds support for the $dimensions() and $unpacked_dimensions()
array functions. Since the argument is only used to get the type information
these functions can always be evaluated at compile time.

For the following functions if the dimension argument is constant or omitted
and the first argument is not dynamic (a string or dynamic array) they will
return the specified information.

  $left(), $right(), $high(), $low(), $increment() and $size()

Dynamic information and a variable second argument will be implement in a
future patch.
This commit is contained in:
Cary R 2013-12-18 18:52:00 -08:00
parent 92e4ca3a92
commit 503a1bf6d7
2 changed files with 287 additions and 30 deletions

View File

@ -1923,6 +1923,173 @@ NetExpr* NetESFunc::evaluate_min_max_(ID id, const NetExpr*arg0_,
return res;
}
NetEConst* NetESFunc::evaluate_countbits_(const NetExpr* /*arg0*/,
const NetExpr* /*arg1*/) const
{
return 0;
}
NetEConst* NetESFunc::evaluate_countones_(const NetExpr* /*arg*/) const
{
return 0;
}
/* Get the total number of dimensions for the given expression. */
NetEConst* NetESFunc::evaluate_dimensions_(const NetExpr*arg) const
{
const NetESignal*esig = dynamic_cast<const NetESignal*>(arg);
long res = 0;
if (esig != 0) {
const NetNet *sig = esig->sig();
res = sig->packed_dimensions() + sig->unpacked_dimensions();
/* Icarus does not think a string has a packed size so to
* make these routines work correct add one if this is a
* string data type. */
if (sig->data_type() == IVL_VT_STRING) {
assert(sig->packed_dimensions() == 0);
res += 1;
}
}
/* Return the result as an integer sized constant. */
return new NetEConst(verinum(verinum(res), integer_width));
}
NetEConst* NetESFunc::evaluate_isunknown_(const NetExpr* /*arg*/) const
{
return 0;
}
NetEConst* NetESFunc::evaluate_onehot_(const NetExpr* /*arg*/) const
{
return 0;
}
NetEConst* NetESFunc::evaluate_onehot0_(const NetExpr* /*arg*/) const
{
return 0;
}
/* Get the number of unpacked dimensions for the given expression. */
NetEConst* NetESFunc::evaluate_unpacked_dimensions_(const NetExpr*arg) const
{
const NetESignal*esig = dynamic_cast<const NetESignal*>(arg);
long res = 0;
if (esig != 0) {
const NetNet *sig = esig->sig();
res = sig->unpacked_dimensions();
}
/* Return the result as an integer sized constant. */
return new NetEConst(verinum(verinum(res), integer_width));
}
/* This code assumes that the dimension value will fit in a long.
* Return true if no constant dimension value is available. */
static bool check_dimension(const NetExpr*dim_expr, long &dim)
{
const NetEConst*dimi = dynamic_cast<const NetEConst*>(dim_expr);
const NetECReal*dimr = dynamic_cast<const NetECReal*>(dim_expr);
if (dimi == 0 && dimr == 0) return true;
if (dimi) dim = dimi->value().as_long();
if (dimr) dim = dimr->value().as_long();
return false;
}
/* Get the left and right values for the argument at the given dimension
* if it exists. Return true if no values are available. */
static bool get_array_info(const NetExpr*arg, long dim,
long &left, long &right, bool&defer)
{
/* The argument must be a signal that has enough dimensions. */
const NetESignal*esig = dynamic_cast<const NetESignal*>(arg);
if (esig == 0) return true;
const NetNet *sig = esig->sig();
/* A string or dynamic array must be handled by the run time. */
defer = false;
switch (sig->data_type()) {
case IVL_VT_DARRAY:
case IVL_VT_STRING:
defer = true;
return true;
break;
default:
break;
}
long pdims = sig->packed_dimensions();
long updims = sig->unpacked_dimensions();
if (dim > (pdims + updims)) return true;
/* Get the appropriate unpacked or packed dimension information. */
if (dim > updims) {
const vector<netrange_t>&dim_vals = sig->packed_dims();
const netrange_t&range = dim_vals[dim-updims-1];
left = range.get_msb();
right = range.get_lsb();
} else {
const vector<netrange_t>&dim_vals = sig->unpacked_dims();
const netrange_t&range = dim_vals[dim-1];
left = range.get_msb();
right = range.get_lsb();
}
return false;
}
/* Calculate the array property functions. */
NetEConst* NetESFunc::evaluate_array_funcs_(ID id,
const NetExpr*arg0,
const NetExpr*arg1) const
{
long dim = 0;
/* Check to see if the dimension argument is constant. */
if (check_dimension(arg1, dim)) return 0;
/* If dimension is less than 1 return undefined. */
if (dim < 1) {
return new NetEConst(verinum(verinum::Vx, integer_width));
}
/* Get the left/right information for this dimension if it exists. */
long left = 0;
long right = 0;
bool defer;
if (get_array_info(arg0, dim, left, right, defer)) {
/* If this is a string or dynamic array defer this function
* call since the left/right information is dynamic and is
* not available yet. */
if (defer) return 0;
return new NetEConst(verinum(verinum::Vx, integer_width));
}
/* Calculate the appropriate array function result. */
long res;
switch (id) {
case HIGH:
res = (right > left) ? right : left;
break;
case INCR:
res = (right > left) ? -1 : 1;
break;
case LEFT:
res = left;
break;
case LOW:
res = (right > left) ? left : right;
break;
case RIGHT:
res = right;
break;
case SIZE:
res = (right > left) ? right - left : left - right;
res += 1;
break;
default:
res = 0;
assert(0);
}
/* Return the result as an integer sized constant. */
return new NetEConst(verinum(verinum(res), integer_width));
}
/* Make a constant one value that can be used by the one argument
* array properties calls. */
const NetEConst* NetESFunc::const_one_ = new NetEConst(verinum(1U, 32U));
NetExpr* NetESFunc::evaluate_one_arg_(ID id, const NetExpr*arg) const
{
switch (id) {
@ -1930,21 +2097,51 @@ NetExpr* NetESFunc::evaluate_one_arg_(ID id, const NetExpr*arg) const
return evaluate_abs_(arg);
case CLOG2:
return evaluate_clog2_(arg);
case CTONES:
return evaluate_countones_(arg);
case DIMS:
return evaluate_dimensions_(arg);
/* The array functions are handled together. */
case HIGH:
case INCR:
case LEFT:
case LOW:
case RIGHT:
case SIZE:
return evaluate_array_funcs_(id, arg, const_one_);
case ISUNKN:
return evaluate_isunknown_(arg);
case ITOR:
return evaluate_itor_(arg);
case ONEHT:
return evaluate_onehot_(arg);
case ONEHT0:
return evaluate_onehot0_(arg);
case RTOI:
return evaluate_rtoi_(arg);
case UPDIMS:
return evaluate_unpacked_dimensions_(arg);
default:
return evaluate_math_one_arg_(id, arg);
}
}
NetExpr* NetESFunc::evaluate_two_arg_(ID id, const NetExpr*arg0,
const NetExpr*arg1) const
const NetExpr*arg1) const
{
switch (id) {
case MIN:
case CTBITS:
return evaluate_countbits_(arg0, arg1);
/* The array functions are handled together. */
case HIGH:
case INCR:
case LEFT:
case LOW:
case RIGHT:
case SIZE:
return evaluate_array_funcs_(id, arg0, arg1);
case MAX:
case MIN:
return evaluate_min_max_(id, arg0, arg1);
default:
return evaluate_math_two_arg_(id, arg0, arg1);
@ -1990,6 +2187,31 @@ NetESFunc::ID NetESFunc::built_in_id_() const
built_in_func["$tanh" ] = TANH;
}
/* These are available in 1800-2005 and later. */
if (funcs_need_init && (generation_flag >= GN_VER2005_SV)) {
built_in_func["$dimensions" ] = DIMS;
built_in_func["$high" ] = HIGH;
built_in_func["$increment" ] = INCR;
built_in_func["$isunknown" ] = ISUNKN;
built_in_func["$left" ] = LEFT;
built_in_func["$low" ] = LOW;
built_in_func["$onehot" ] = ONEHT;
built_in_func["$onehot0" ] = ONEHT0;
built_in_func["$right" ] = RIGHT;
built_in_func["$size" ] = SIZE;
built_in_func["$unpacked_dimensions" ] = UPDIMS;
}
/* These are available in 1800-2009 and later. */
if (funcs_need_init && (generation_flag >= GN_VER2009)) {
built_in_func["$countones" ] = CTONES;
}
/* These are available in 1800-2012 and later. */
if (funcs_need_init && (generation_flag >= GN_VER2012)) {
built_in_func["$countbits" ] = CTBITS;
}
/* These are available in Verilog-A as Icarus extensions or if the
* Icarus misc flag was given. */
if (funcs_need_init && (gn_verilog_ams_flag || gn_icarus_misc_flag)) {
@ -2045,6 +2267,7 @@ NetExpr* NetESFunc::eval_tree()
<< " arguments." << endl;
return 0;
}
// HERE: Need to add support for a multi argument $countbits().
cerr << get_fileline() << ": sorry: functions with "
<< parms_.size() << " arguments are not supported: "
<< name_ << "()." << endl;

View File

@ -4176,37 +4176,53 @@ class NetESFunc : public NetExpr {
*/
enum ID { NOT_BUILT_IN = 0x0,
/* Available in all version of Verilog/SystemVerilog. */
ITOR = 0x00020001, /* $itor takes one argument. */
RTOI = 0x00020002, /* $rtoi takes one argument. */
ITOR = 0x00020001, /* $itor takes one argument. */
RTOI = 0x00020002, /* $rtoi takes one argument. */
/* Available in Verilog 2005 and later. */
ACOS = 0x00020003, /* $acos takes one argument. */
ACOSH = 0x00020004, /* $acosh takes one argument. */
ASIN = 0x00020005, /* $asin takes one argument. */
ASINH = 0x00020006, /* $asinh takes one argument. */
ATAN = 0x00020007, /* $atan takes one argument. */
ATANH = 0x00020008, /* $atanh takes one argument. */
ATAN2 = 0x00040009, /* $atan2 takes two argument. */
CEIL = 0x0002000a, /* $ceil takes one argument. */
CLOG2 = 0x0002000b, /* $clog2 takes one argument. */
COS = 0x0002000c, /* $cos takes one argument. */
COSH = 0x0002000d, /* $cosh takes one argument. */
EXP = 0x0002000e, /* $exp takes one argument. */
FLOOR = 0x0002000f, /* $floor takes one argument. */
HYPOT = 0x00040010, /* $hypot takes two argument. */
LN = 0x00020011, /* $ln takes one argument. */
LOG10 = 0x00020012, /* $log10 takes one argument. */
POW = 0x00040013, /* $pow takes two argument. */
SIN = 0x00020014, /* $sin takes one argument. */
SINH = 0x00020015, /* $sinh takes one argument. */
SQRT = 0x00020016, /* $sqrt takes one argument. */
TAN = 0x00020017, /* $tan takes one argument. */
TANH = 0x00020018, /* $tanh takes one argument. */
ACOS = 0x00020003, /* $acos takes one argument. */
ACOSH = 0x00020004, /* $acosh takes one argument. */
ASIN = 0x00020005, /* $asin takes one argument. */
ASINH = 0x00020006, /* $asinh takes one argument. */
ATAN = 0x00020007, /* $atan takes one argument. */
ATANH = 0x00020008, /* $atanh takes one argument. */
ATAN2 = 0x00040009, /* $atan2 takes two argument. */
CEIL = 0x0002000a, /* $ceil takes one argument. */
CLOG2 = 0x0002000b, /* $clog2 takes one argument. */
COS = 0x0002000c, /* $cos takes one argument. */
COSH = 0x0002000d, /* $cosh takes one argument. */
EXP = 0x0002000e, /* $exp takes one argument. */
FLOOR = 0x0002000f, /* $floor takes one argument. */
HYPOT = 0x00040010, /* $hypot takes two argument. */
LN = 0x00020011, /* $ln takes one argument. */
LOG10 = 0x00020012, /* $log10 takes one argument. */
POW = 0x00040013, /* $pow takes two argument. */
SIN = 0x00020014, /* $sin takes one argument. */
SINH = 0x00020015, /* $sinh takes one argument. */
SQRT = 0x00020016, /* $sqrt takes one argument. */
TAN = 0x00020017, /* $tan takes one argument. */
TANH = 0x00020018, /* $tanh takes one argument. */
/* Added in SystemVerilog 2005 and later. */
DIMS = 0x00020019, /* $dimensions takes one argument. */
HIGH = 0x0006001a, /* $high takes one or two arguments. */
INCR = 0x0006001b, /* $increment takes one or two arguments. */
LEFT = 0x0006001c, /* $left takes one or two arguments. */
LOW = 0x0006001d, /* $low takes one or two arguments. */
RIGHT = 0x0006001e, /* $right takes one or two arguments. */
SIZE = 0x0006001f, /* $size takes one or two arguments. */
UPDIMS = 0x00020020, /* $unpacked_dimensions takes one argument. */
ISUNKN = 0x00020021, /* $isunknown takes one argument. */
ONEHT = 0x00020022, /* $onehot takes one argument. */
ONEHT0 = 0x00020023, /* $onehot0 takes one argument. */
/* Added in SystemVerilog 2009 and later. */
CTONES = 0x00020024, /* $countones takes one argument. */
/* Added in SystemVerilog 2012 and later. */
CTBITS = 0xfffe0025, /* $countbits takes one or more arguments. */
/* Added as Icarus extensions to Verilog-A. */
ABS = 0x00020019, /* $abs takes one argument. */
MAX = 0x0004001a, /* $max takes two argument. */
MIN = 0x0004001b, /* $min takes two argument. */
ABS = 0x00020026, /* $abs takes one argument. */
MAX = 0x00040027, /* $max takes two argument. */
MIN = 0x00040028, /* $min takes two argument. */
/* A dummy value to properly close the enum. */
DUMMY = 0xffffffff };
DUMMY = 0xffffffff };
bool takes_nargs_(ID func, unsigned nargs) {
if (nargs > 15) nargs = 15;
@ -4237,6 +4253,24 @@ class NetESFunc : public NetExpr {
NetExpr* evaluate_min_max_(ID id, const NetExpr*arg0,
const NetExpr*arg1) const;
/* Constant SystemVerilog functions. */
NetEConst* evaluate_countones_(const NetExpr*arg) const;
NetEConst* evaluate_dimensions_(const NetExpr*arg) const;
NetEConst* evaluate_isunknown_(const NetExpr*arg) const;
NetEConst* evaluate_onehot_(const NetExpr*arg) const;
NetEConst* evaluate_onehot0_(const NetExpr*arg) const;
NetEConst* evaluate_unpacked_dimensions_(const NetExpr*arg) const;
/* This value is used as a default when the array functions are
* called with a single argument. */
static const NetEConst*const_one_;
NetEConst* evaluate_array_funcs_(ID id,
const NetExpr*arg0,
const NetExpr*arg1) const;
NetEConst* evaluate_countbits_(const NetExpr*arg0,
const NetExpr*arg1) const;
public:
bool is_built_in() const { return built_in_id_() != NOT_BUILT_IN; };