Add $clog2 function.
This patch adds the $clog2 system function. It also makes this function work as a constant function. The runtime version still needs to be updated to use an integer based version instead of the current double based method. The double method suffers from rounding errors.
This commit is contained in:
parent
e501bbdd27
commit
768633e464
23
PExpr.cc
23
PExpr.cc
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 1998-2007 Stephen Williams <steve@icarus.com>
|
* Copyright (c) 1998-2008 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
|
||||||
|
|
@ -117,6 +117,26 @@ PECallFunction::~PECallFunction()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool PECallFunction::is_constant(Module*mod) const
|
||||||
|
{
|
||||||
|
/* Only $clog2 can be a constant system function. */
|
||||||
|
if (peek_tail_name(path_)[0] == '$') {
|
||||||
|
if (strcmp(peek_tail_name(path_).str(), "$clog2") == 0) {
|
||||||
|
if (parms_.count() != 1 || parms_[0] == 0) {
|
||||||
|
cerr << get_fileline() << ": error: $clog2 takes a "
|
||||||
|
"single argument." << endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
/* If the argument is constant $clog2 is constant. */
|
||||||
|
return parms_[0]->is_constant(mod);
|
||||||
|
}
|
||||||
|
return false; /* Most system functions are not constant. */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Checking for constant user functions goes here. */
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
PEConcat::PEConcat(const svector<PExpr*>&p, PExpr*r)
|
PEConcat::PEConcat(const svector<PExpr*>&p, PExpr*r)
|
||||||
: parms_(p), repeat_(r)
|
: parms_(p), repeat_(r)
|
||||||
{
|
{
|
||||||
|
|
@ -299,4 +319,3 @@ bool PEUnary::is_constant(Module*m) const
|
||||||
{
|
{
|
||||||
return expr_->is_constant(m);
|
return expr_->is_constant(m);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
3
PExpr.h
3
PExpr.h
|
|
@ -703,6 +703,8 @@ class PECallFunction : public PExpr {
|
||||||
explicit PECallFunction(perm_string n);
|
explicit PECallFunction(perm_string n);
|
||||||
~PECallFunction();
|
~PECallFunction();
|
||||||
|
|
||||||
|
virtual bool is_constant(Module*) const;
|
||||||
|
|
||||||
virtual void dump(ostream &) const;
|
virtual void dump(ostream &) const;
|
||||||
|
|
||||||
virtual NetNet* elaborate_net(Design*des, NetScope*scope,
|
virtual NetNet* elaborate_net(Design*des, NetScope*scope,
|
||||||
|
|
@ -714,6 +716,7 @@ class PECallFunction : public PExpr {
|
||||||
Link::strength_t drive1) const;
|
Link::strength_t drive1) const;
|
||||||
virtual NetExpr*elaborate_expr(Design*des, NetScope*scope,
|
virtual NetExpr*elaborate_expr(Design*des, NetScope*scope,
|
||||||
int expr_wid, bool sys_task_arg) const;
|
int expr_wid, bool sys_task_arg) const;
|
||||||
|
virtual NetExpr*elaborate_pexpr(Design*des, NetScope*sc) const;
|
||||||
|
|
||||||
virtual unsigned test_width(Design*des, NetScope*scope,
|
virtual unsigned test_width(Design*des, NetScope*scope,
|
||||||
unsigned min, unsigned lval,
|
unsigned min, unsigned lval,
|
||||||
|
|
|
||||||
|
|
@ -267,3 +267,40 @@ NetExpr*PEUnary::elaborate_pexpr (Design*des, NetScope*scope) const
|
||||||
|
|
||||||
return tmp;
|
return tmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Reuse the routine from eval_tree.cc. */
|
||||||
|
NetExpr* evaluate_clog2(NetExpr*arg);
|
||||||
|
|
||||||
|
NetExpr* PECallFunction::elaborate_pexpr(Design*des, NetScope*scope) const
|
||||||
|
{
|
||||||
|
/* For now only $clog2 can be a constant system function. */
|
||||||
|
if (peek_tail_name(path_)[0] == '$') {
|
||||||
|
if (strcmp(peek_tail_name(path_).str(), "$clog2") == 0) {
|
||||||
|
if (parms_.count() != 1 || parms_[0] == 0) {
|
||||||
|
cerr << get_fileline() << ": error: $clog2 takes a "
|
||||||
|
"single argument." << endl;
|
||||||
|
des->errors += 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
NetExpr*arg = parms_[0]->elaborate_pexpr(des, scope);
|
||||||
|
eval_expr(arg);
|
||||||
|
NetExpr*rtn = evaluate_clog2(arg);
|
||||||
|
delete arg;
|
||||||
|
if (rtn != 0) {
|
||||||
|
rtn->set_line(*this);
|
||||||
|
return rtn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cerr << get_fileline() << ": error: this is not a constant "
|
||||||
|
"system function (" << *this << ")." << endl;
|
||||||
|
des->errors += 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Constant user function code goes here. */
|
||||||
|
cerr << get_fileline() << ": sorry: constant user functions are not "
|
||||||
|
"currently supported." << endl;
|
||||||
|
des->errors += 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
||||||
54
eval_tree.cc
54
eval_tree.cc
|
|
@ -22,6 +22,7 @@
|
||||||
|
|
||||||
# include <iostream>
|
# include <iostream>
|
||||||
# include <cstdlib>
|
# include <cstdlib>
|
||||||
|
# include <math.h>
|
||||||
|
|
||||||
# include "netlist.h"
|
# include "netlist.h"
|
||||||
# include "ivl_assert.h"
|
# include "ivl_assert.h"
|
||||||
|
|
@ -1607,3 +1608,56 @@ NetEConst* NetEUReduce::eval_tree(int prune_to_width)
|
||||||
|
|
||||||
return new NetEConst(verinum(res, 1));
|
return new NetEConst(verinum(res, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NetExpr* evaluate_clog2(NetExpr*arg)
|
||||||
|
{
|
||||||
|
NetEConst*tmpi = dynamic_cast<NetEConst *>(arg);
|
||||||
|
NetECReal*tmpr = dynamic_cast<NetECReal *>(arg);
|
||||||
|
bool is_neg = false;
|
||||||
|
if (tmpi || tmpr) {
|
||||||
|
verinum arg;
|
||||||
|
if (tmpi) {
|
||||||
|
arg = tmpi->value();
|
||||||
|
} else {
|
||||||
|
arg = verinum(tmpr->value().as_double(), true);
|
||||||
|
}
|
||||||
|
uint64_t res = 0;
|
||||||
|
if (arg.is_negative()) is_neg = true;
|
||||||
|
arg.has_sign(false); // $unsigned()
|
||||||
|
if (!arg.is_zero()) {
|
||||||
|
arg = arg - verinum((uint64_t)1, 1);
|
||||||
|
while (!arg.is_zero()) {
|
||||||
|
res += 1;
|
||||||
|
arg = arg >> 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_neg && res < 32) res = 32;
|
||||||
|
NetEConst*rtn = new NetEConst(verinum(res, 32));
|
||||||
|
return rtn;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetExpr* NetESFunc::eval_tree(int prune_to_width)
|
||||||
|
{
|
||||||
|
/* For now only $clog2 can be a constant system function. */
|
||||||
|
if (strcmp(name(), "$clog2") == 0) {
|
||||||
|
if (nparms() != 1 || parm(0) == 0) {
|
||||||
|
cerr << get_fileline() << ": error: $clog2 takes a single "
|
||||||
|
"argument." << endl;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
NetExpr*rtn = evaluate_clog2(parm(0));
|
||||||
|
if (rtn != 0) {
|
||||||
|
rtn->set_line(*this);
|
||||||
|
if (debug_eval_tree) {
|
||||||
|
cerr << get_fileline() << ": debug: Evaluate "
|
||||||
|
"constant $clog2()." << endl;
|
||||||
|
}
|
||||||
|
return rtn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -3442,6 +3442,8 @@ class NetESFunc : public NetExpr {
|
||||||
NetExpr* parm(unsigned idx);
|
NetExpr* parm(unsigned idx);
|
||||||
const NetExpr* parm(unsigned idx) const;
|
const NetExpr* parm(unsigned idx) const;
|
||||||
|
|
||||||
|
virtual NetExpr* eval_tree(int prune_to_width = -1);
|
||||||
|
|
||||||
virtual ivl_variable_type_t expr_type() const;
|
virtual ivl_variable_type_t expr_type() const;
|
||||||
virtual NexusSet* nex_input(bool rem_out = true);
|
virtual NexusSet* nex_input(bool rem_out = true);
|
||||||
virtual bool set_width(unsigned, bool last_chance);
|
virtual bool set_width(unsigned, bool last_chance);
|
||||||
|
|
|
||||||
125
verinum.cc
125
verinum.cc
|
|
@ -152,6 +152,121 @@ verinum::verinum(uint64_t val, unsigned n)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* The second argument is not used! It is there to make this
|
||||||
|
* constructor unique. */
|
||||||
|
verinum::verinum(double val, bool dummy)
|
||||||
|
: has_len_(false), has_sign_(true), string_flag_(false)
|
||||||
|
{
|
||||||
|
bool is_neg = false;
|
||||||
|
double fraction;
|
||||||
|
int exponent;
|
||||||
|
const unsigned BITS_IN_LONG = 8*sizeof(long);
|
||||||
|
|
||||||
|
/* We return `bx for a NaN. */
|
||||||
|
if (val != val) {
|
||||||
|
nbits_ = 1;
|
||||||
|
bits_ = new V[nbits_];
|
||||||
|
bits_[0] = Vx;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We return 'b1 for + infinity or 'b0 for - infinity. */
|
||||||
|
if (val && (val == 0.5*val)) {
|
||||||
|
nbits_ = 1;
|
||||||
|
bits_ = new V[nbits_];
|
||||||
|
if (val > 0) bits_[0] = V1;
|
||||||
|
else bits_[0] = V0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert to a positive result. */
|
||||||
|
if (val < 0.0) {
|
||||||
|
is_neg = true;
|
||||||
|
val = -val;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Get the exponent and fractional part of the number. */
|
||||||
|
fraction = frexp(val, &exponent);
|
||||||
|
nbits_ = exponent+1;
|
||||||
|
bits_ = new V[nbits_];
|
||||||
|
const verinum const_one(1);
|
||||||
|
|
||||||
|
/* If the value is small enough just use lround(). */
|
||||||
|
if (nbits_ <= BITS_IN_LONG) {
|
||||||
|
long sval = lround(val);
|
||||||
|
if (is_neg) sval = -sval;
|
||||||
|
for (unsigned idx = 0; idx < nbits_; idx += 1) {
|
||||||
|
bits_[idx] = (sval&1) ? V1 : V0;
|
||||||
|
sval >>= 1;
|
||||||
|
}
|
||||||
|
/* Trim the result. */
|
||||||
|
signed_trim();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned nwords = (exponent-1)/BITS_IN_LONG;
|
||||||
|
|
||||||
|
fraction = ldexp(fraction, (exponent-1) % BITS_IN_LONG + 1);
|
||||||
|
|
||||||
|
if (nwords == 0) {
|
||||||
|
unsigned long bits = (unsigned long) fraction;
|
||||||
|
fraction = fraction - (double) bits;
|
||||||
|
for (unsigned idx = 0; idx < nbits_; idx += 1) {
|
||||||
|
bits_[idx] = (bits&1) ? V1 : V0;
|
||||||
|
bits >>= 1;
|
||||||
|
}
|
||||||
|
if (fraction >= 0.5) *this = *this + const_one;
|
||||||
|
} else {
|
||||||
|
for (int wd = nwords; wd >= 0; wd -= 1) {
|
||||||
|
unsigned long bits = (unsigned long) fraction;
|
||||||
|
fraction = fraction - (double) bits;
|
||||||
|
unsigned max = (wd+1)*BITS_IN_LONG;
|
||||||
|
if (max > nbits_) max = nbits_;
|
||||||
|
for (unsigned idx = wd*BITS_IN_LONG; idx < max; idx += 1) {
|
||||||
|
bits_[idx] = (bits&1) ? V1 : V0;
|
||||||
|
bits >>= 1;
|
||||||
|
}
|
||||||
|
fraction = ldexp(fraction, BITS_IN_LONG);
|
||||||
|
}
|
||||||
|
if (fraction >= ldexp(0.5, BITS_IN_LONG)) *this = *this + const_one;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Convert a negative number if needed. */
|
||||||
|
if (is_neg) {
|
||||||
|
*this = v_not(*this) + const_one;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Trim the result. */
|
||||||
|
signed_trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* This is used by the double constructor above. It is needed to remove
|
||||||
|
* extra sign bits that can occur when calculating a negative value. */
|
||||||
|
void verinum::signed_trim()
|
||||||
|
{
|
||||||
|
/* Do we have any extra digits? */
|
||||||
|
unsigned tlen = nbits_-1;
|
||||||
|
verinum::V sign = bits_[tlen];
|
||||||
|
while ((tlen > 0) && (bits_[tlen] == sign)) tlen -= 1;
|
||||||
|
|
||||||
|
/* tlen now points to the first digit that is not the sign.
|
||||||
|
* or bit 0. Set the length to include this bit and one proper
|
||||||
|
* sign bit if needed. */
|
||||||
|
if (bits_[tlen] != sign) tlen += 1;
|
||||||
|
tlen += 1;
|
||||||
|
|
||||||
|
/* Trim the bits if needed. */
|
||||||
|
if (tlen < nbits_) {
|
||||||
|
V* tbits = new V[tlen];
|
||||||
|
for (unsigned idx = 0; idx < tlen; idx += 1)
|
||||||
|
tbits[idx] = bits_[idx];
|
||||||
|
delete[] bits_;
|
||||||
|
bits_ = tbits;
|
||||||
|
nbits_ = tlen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
verinum::verinum(const verinum&that)
|
verinum::verinum(const verinum&that)
|
||||||
{
|
{
|
||||||
string_flag_ = that.string_flag_;
|
string_flag_ = that.string_flag_;
|
||||||
|
|
@ -336,14 +451,9 @@ double verinum::as_double() const
|
||||||
{
|
{
|
||||||
if (nbits_ == 0) return 0.0;
|
if (nbits_ == 0) return 0.0;
|
||||||
|
|
||||||
/* Do we have a signed value? */
|
|
||||||
bool signed_flag = false;
|
|
||||||
if (bits_[nbits_-1] == V1) {
|
|
||||||
signed_flag = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
double val = 0.0;
|
double val = 0.0;
|
||||||
if (signed_flag) {
|
/* Do we have/want a signed value? */
|
||||||
|
if (has_sign_ && bits_[nbits_-1] == V1) {
|
||||||
V carry = V1;
|
V carry = V1;
|
||||||
for (unsigned idx = 0; idx < nbits_; idx += 1) {
|
for (unsigned idx = 0; idx < nbits_; idx += 1) {
|
||||||
V sum = add_with_carry(~bits_[idx], V0, carry);
|
V sum = add_with_carry(~bits_[idx], V0, carry);
|
||||||
|
|
@ -351,7 +461,6 @@ double verinum::as_double() const
|
||||||
val += pow(2.0, (double)idx);
|
val += pow(2.0, (double)idx);
|
||||||
}
|
}
|
||||||
val *= -1.0;
|
val *= -1.0;
|
||||||
// val = (double) as_long();
|
|
||||||
} else {
|
} else {
|
||||||
for (unsigned idx = 0; idx < nbits_; idx += 1) {
|
for (unsigned idx = 0; idx < nbits_; idx += 1) {
|
||||||
if (bits_[idx] == V1)
|
if (bits_[idx] == V1)
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ class verinum {
|
||||||
verinum(const V*v, unsigned nbits, bool has_len =true);
|
verinum(const V*v, unsigned nbits, bool has_len =true);
|
||||||
verinum(V, unsigned nbits =1, bool has_len =true);
|
verinum(V, unsigned nbits =1, bool has_len =true);
|
||||||
verinum(uint64_t val, unsigned bits);
|
verinum(uint64_t val, unsigned bits);
|
||||||
|
verinum(double val, bool dummy);
|
||||||
verinum(const verinum&);
|
verinum(const verinum&);
|
||||||
|
|
||||||
// Create a signed number, with an unspecified number of bits.
|
// Create a signed number, with an unspecified number of bits.
|
||||||
|
|
@ -93,6 +94,8 @@ class verinum {
|
||||||
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;
|
||||||
|
private:
|
||||||
|
void signed_trim();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
V* bits_;
|
V* bits_;
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ sys_finish.o sys_icarus.o sys_plusargs.o sys_random.o sys_random_mti.o \
|
||||||
sys_readmem.o sys_readmem_lex.o sys_scanf.o sys_sdf.o \
|
sys_readmem.o sys_readmem_lex.o sys_scanf.o sys_sdf.o \
|
||||||
sys_time.o sys_vcd.o sys_vcdoff.o vcd_priv.o \
|
sys_time.o sys_vcd.o sys_vcdoff.o vcd_priv.o \
|
||||||
mt19937int.o sys_priv.o sdf_lexor.o sdf_parse.o stringheap.o \
|
mt19937int.o sys_priv.o sdf_lexor.o sdf_parse.o stringheap.o \
|
||||||
vams_simparam.o
|
sys_clog2.o vams_simparam.o
|
||||||
|
|
||||||
ifeq (@HAVE_LIBZ@,yes)
|
ifeq (@HAVE_LIBZ@,yes)
|
||||||
ifeq (@HAVE_LIBBZ2@,yes)
|
ifeq (@HAVE_LIBBZ2@,yes)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,122 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2008 Cary R. (cygcary@yahoo.com)
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 2 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program; if not, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <vpi_user.h>
|
||||||
|
#include "sys_priv.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check that the function is called with the correct argument.
|
||||||
|
*/
|
||||||
|
static PLI_INT32 sys_clog2_compiletf(PLI_BYTE8 *name)
|
||||||
|
{
|
||||||
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||||
|
assert(callh != 0);
|
||||||
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||||
|
vpiHandle arg;
|
||||||
|
(void) name; // Not used!
|
||||||
|
|
||||||
|
/* We must have an argument. */
|
||||||
|
if (argv == 0) {
|
||||||
|
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||||
|
(int)vpi_get(vpiLineNo, callh));
|
||||||
|
vpi_printf("$clog2 requires one numeric argument.\n");
|
||||||
|
vpi_control(vpiFinish, 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The argument must be numeric. */
|
||||||
|
arg = vpi_scan(argv);
|
||||||
|
if (! is_numeric_obj(arg)) {
|
||||||
|
vpi_printf("ERROR: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||||
|
(int)vpi_get(vpiLineNo, callh));
|
||||||
|
vpi_printf("The first argument to $clog2 must be numeric.\n");
|
||||||
|
vpi_control(vpiFinish, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We can have a maximum of one argument. */
|
||||||
|
if (vpi_scan(argv) != 0) {
|
||||||
|
char msg [64];
|
||||||
|
snprintf(msg, 64, "ERROR: %s line %d:",
|
||||||
|
vpi_get_str(vpiFile, callh),
|
||||||
|
(int)vpi_get(vpiLineNo, callh));
|
||||||
|
|
||||||
|
unsigned argc = 1;
|
||||||
|
while (vpi_scan(argv)) argc += 1;
|
||||||
|
|
||||||
|
vpi_printf("%s $clog2 takes at most one argument.\n", msg);
|
||||||
|
vpi_printf("%*s Found %u extra argument%s.\n",
|
||||||
|
strlen(msg), " ", argc, argc == 1 ? "" : "s");
|
||||||
|
vpi_control(vpiFinish, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PLI_INT32 sys_clog2_calltf(PLI_BYTE8 *name)
|
||||||
|
{
|
||||||
|
vpiHandle callh = vpi_handle(vpiSysTfCall, 0);
|
||||||
|
vpiHandle argv = vpi_iterate(vpiArgument, callh);
|
||||||
|
vpiHandle arg;
|
||||||
|
s_vpi_value val;
|
||||||
|
(void) name; // Not used!/
|
||||||
|
|
||||||
|
/* Get the argument. */
|
||||||
|
arg = vpi_scan(argv);
|
||||||
|
val.format = vpiRealVal;
|
||||||
|
vpi_get_value(arg, &val);
|
||||||
|
vpi_free_object(argv);
|
||||||
|
|
||||||
|
/* For now we don't support a negative value! */
|
||||||
|
if (val.value.real < 0.0) {
|
||||||
|
vpi_printf("SORRY: %s line %d: ", vpi_get_str(vpiFile, callh),
|
||||||
|
(int)vpi_get(vpiLineNo, callh));
|
||||||
|
vpi_printf("$clog2 does not currently support negative values.\n");
|
||||||
|
vpi_control(vpiFinish, 1);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Return the value to the system. */
|
||||||
|
val.format = vpiIntVal;
|
||||||
|
if (val.value.real == 0.0)
|
||||||
|
val.value.integer = 0;
|
||||||
|
else
|
||||||
|
val.value.integer = ceil(log(floor(val.value.real+0.5))/M_LN2);
|
||||||
|
vpi_put_value(callh, &val, 0, vpiNoDelay);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Register the function with Verilog.
|
||||||
|
*/
|
||||||
|
void sys_clog2_register(void)
|
||||||
|
{
|
||||||
|
s_vpi_systf_data tf_data;
|
||||||
|
|
||||||
|
tf_data.type = vpiSysFunc;
|
||||||
|
tf_data.sysfunctype = vpiIntFunc;
|
||||||
|
tf_data.calltf = sys_clog2_calltf;
|
||||||
|
tf_data.compiletf = sys_clog2_compiletf;
|
||||||
|
tf_data.sizetf = 0;
|
||||||
|
tf_data.tfname = "$clog2";
|
||||||
|
tf_data.user_data = 0;
|
||||||
|
vpi_register_systf(&tf_data);
|
||||||
|
}
|
||||||
|
|
@ -38,6 +38,7 @@ extern void sys_time_register();
|
||||||
extern void sys_vcd_register();
|
extern void sys_vcd_register();
|
||||||
extern void sys_vcdoff_register();
|
extern void sys_vcdoff_register();
|
||||||
extern void sys_special_register();
|
extern void sys_special_register();
|
||||||
|
extern void sys_clog2_register();
|
||||||
extern void vams_simparam_register();
|
extern void vams_simparam_register();
|
||||||
|
|
||||||
#ifdef HAVE_LIBZ
|
#ifdef HAVE_LIBZ
|
||||||
|
|
@ -181,6 +182,7 @@ void (*vlog_startup_routines[])() = {
|
||||||
sys_lxt_or_vcd_register,
|
sys_lxt_or_vcd_register,
|
||||||
sys_sdf_register,
|
sys_sdf_register,
|
||||||
sys_special_register,
|
sys_special_register,
|
||||||
|
sys_clog2_register,
|
||||||
vams_simparam_register,
|
vams_simparam_register,
|
||||||
0
|
0
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -14,6 +14,7 @@ $dist_poisson vpiSysFuncInt
|
||||||
$dist_chi_square vpiSysFuncInt
|
$dist_chi_square vpiSysFuncInt
|
||||||
$dist_t vpiSysFuncInt
|
$dist_t vpiSysFuncInt
|
||||||
$dist_erlang vpiSysFuncInt
|
$dist_erlang vpiSysFuncInt
|
||||||
|
$clog2 vpiSysFuncInt
|
||||||
|
|
||||||
$simparam vpiSysFuncReal
|
$simparam vpiSysFuncReal
|
||||||
$simparam$str vpiSysFuncSized 1024 unsigned
|
$simparam$str vpiSysFuncSized 1024 unsigned
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue