Fix for pr2123173.
Functions that appear in continuous assignment expressions and that have hidden dependencies or side effects need to be re-evaluated whenever any input to the expression changes. This patch adds support in the compiler and vvp runtime to enable this. This is currently activated for any system function call that has no arguments. The user may also force it to be used for any user function by passing the option -gstrict-ca-eval to the compiler driver. This patch also removes the -dautomatic option which was used for gaining confidence in the code that supports automatic tasks and functions. It is believed that the testsuite provides reasonable fault coverage, and further tests can be added if bugs are found.
This commit is contained in:
parent
31d67fcd3e
commit
21f33085f0
|
|
@ -109,7 +109,7 @@ static NetExpr* make_delay_nets(Design*des, NetScope*scope, NetExpr*expr)
|
|||
if (dynamic_cast<NetEConst*> (expr))
|
||||
return expr;
|
||||
|
||||
NetNet*sig = expr->synthesize(des, scope);
|
||||
NetNet*sig = expr->synthesize(des, scope, expr);
|
||||
if (sig == 0) {
|
||||
cerr << expr->get_fileline() << ": error: Expression " << *expr
|
||||
<< " is not suitable for delay expression." << endl;
|
||||
|
|
|
|||
|
|
@ -88,7 +88,6 @@ extern bool debug_elaborate;
|
|||
extern bool debug_elab_pexpr;
|
||||
extern bool debug_synth2;
|
||||
extern bool debug_optimizer;
|
||||
extern bool debug_automatic;
|
||||
|
||||
/* Path to a directory useful for finding subcomponents. */
|
||||
extern const char*basedir;
|
||||
|
|
@ -126,6 +125,11 @@ extern bool gn_verilog_ams_flag;
|
|||
is scalar and the net/register definition is vectored. */
|
||||
extern bool gn_io_range_error_flag;
|
||||
|
||||
/* If this flag is true, then force re-evaluation of user functions
|
||||
in a continuous assignment when any part of the expression is
|
||||
re-evaluated. */
|
||||
extern bool gn_strict_ca_eval_flag;
|
||||
|
||||
/* The bits of these GN_KEYWORDS_* constants define non-intersecting
|
||||
sets of keywords. The compiler enables groups of keywords by setting
|
||||
lexor_keyword_mask with the OR of the bits for the keywords to be
|
||||
|
|
|
|||
|
|
@ -97,6 +97,16 @@ reported as a error. Using \fI-gno-io-range-error\fP will produce a
|
|||
warning instead of a fatal error for the case of a vectored net/register
|
||||
and a scalar port declaration.
|
||||
.TP 8
|
||||
.B -gstrict-ca-eval\fI|\fP-gno-strict-ca-eval
|
||||
The standard requires that if any input to a continuous assignment
|
||||
expression changes value, the entire expression is re-evaluated. By
|
||||
default, parts of the expression that do not depend on the changed
|
||||
input value(s) are not re-evaluated. If an expression contains a call
|
||||
to a function that doesn't depend solely on its input values or that
|
||||
has side effects, the resulting behaviour will differ from that
|
||||
required by the standard. Using \fI-gstrict-ca-eval\fP will force
|
||||
standard compliant behaviour (with some loss in performance).
|
||||
.TP 8
|
||||
.B -I\fIincludedir\fP
|
||||
Append directory \fIincludedir\fP to list of directories searched
|
||||
for Verilog include files. The \fB-I\fP switch may be used many times
|
||||
|
|
|
|||
|
|
@ -37,7 +37,8 @@ const char NOTICE[] =
|
|||
;
|
||||
|
||||
const char HELP[] =
|
||||
"Usage: iverilog [-ESvV] [-B base] [-c cmdfile|-f cmdfile] [-g1|-g2|-g2x]\n"
|
||||
"Usage: iverilog [-ESvV] [-B base] [-c cmdfile|-f cmdfile]\n"
|
||||
" [-g1995|-g2001|-g2005] [-g<feature>]\n"
|
||||
" [-D macro[=defn]] [-I includedir] [-M depfile] [-m module]\n"
|
||||
" [-N file] [-o filename] [-p flag=value]\n"
|
||||
" [-s topmodule] [-t target] [-T min|typ|max]\n"
|
||||
|
|
@ -112,6 +113,7 @@ const char*gen_specify = "no-specify";
|
|||
const char*gen_xtypes = "xtypes";
|
||||
const char*gen_icarus = "icarus-misc";
|
||||
const char*gen_io_range_error = "io-range-error";
|
||||
const char*gen_strict_ca_eval = "no-strict-ca-eval";
|
||||
const char*gen_verilog_ams = "no-verilog-ams";
|
||||
|
||||
/* Boolean: true means use a default include dir, false means don't */
|
||||
|
|
@ -598,6 +600,12 @@ int process_generation(const char*name)
|
|||
else if (strcmp(name,"no-io-range-error") == 0)
|
||||
gen_io_range_error = "no-io-range-error";
|
||||
|
||||
else if (strcmp(name,"strict-ca-eval") == 0)
|
||||
gen_strict_ca_eval = "strict-ca-eval";
|
||||
|
||||
else if (strcmp(name,"no-strict-ca-eval") == 0)
|
||||
gen_strict_ca_eval = "no-strict-ca-eval";
|
||||
|
||||
else if (strcmp(name,"verilog-ams") == 0)
|
||||
gen_verilog_ams = "verilog-ams";
|
||||
|
||||
|
|
@ -617,7 +625,8 @@ int process_generation(const char*name)
|
|||
" std-include | no-std-include\n"
|
||||
" xtypes | no-xtypes\n"
|
||||
" icarus-misc | no-icarus-misc\n"
|
||||
" io-range-error | no-io-range-error\n");
|
||||
" io-range-error | no-io-range-error\n"
|
||||
" strict-ca-eval | no-strict-ca-eval\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
@ -887,6 +896,7 @@ int main(int argc, char **argv)
|
|||
fprintf(iconfig_file, "generation:%s\n", gen_specify);
|
||||
fprintf(iconfig_file, "generation:%s\n", gen_xtypes);
|
||||
fprintf(iconfig_file, "generation:%s\n", gen_io_range_error);
|
||||
fprintf(iconfig_file, "generation:%s\n", gen_strict_ca_eval);
|
||||
fprintf(iconfig_file, "generation:%s\n", gen_verilog_ams);
|
||||
fprintf(iconfig_file, "generation:%s\n", gen_icarus);
|
||||
fprintf(iconfig_file, "warnings:%s\n", warning_flags);
|
||||
|
|
|
|||
12
elaborate.cc
12
elaborate.cc
|
|
@ -122,7 +122,7 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const
|
|||
rval_expr = pad_to_width(rval_expr, lval->vector_width(), *this);
|
||||
}
|
||||
|
||||
NetNet*rval = rval_expr->synthesize(des, scope);
|
||||
NetNet*rval = rval_expr->synthesize(des, scope, rval_expr);
|
||||
|
||||
if (rval == 0) {
|
||||
cerr << get_fileline() << ": internal error: "
|
||||
|
|
@ -731,7 +731,7 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const
|
|||
ex->test_width(des, scope, 0, use_width, tmp_type, flag);
|
||||
NetExpr*tmp = elab_and_eval(des, scope, ex,
|
||||
use_width, use_width);
|
||||
sig = tmp->synthesize(des, scope);
|
||||
sig = tmp->synthesize(des, scope, tmp);
|
||||
delete tmp;
|
||||
}
|
||||
|
||||
|
|
@ -1204,7 +1204,7 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const
|
|||
<< "too complicated for elaboration." << endl;
|
||||
continue;
|
||||
}
|
||||
sig = tmp_expr->synthesize(des, scope);
|
||||
sig = tmp_expr->synthesize(des, scope, tmp_expr);
|
||||
if (sig == 0) {
|
||||
cerr << pins[idx]->get_fileline()
|
||||
<< ": internal error: Port expression "
|
||||
|
|
@ -1674,7 +1674,7 @@ void PGModule::elaborate_udp_(Design*des, PUdp*udp, NetScope*scope) const
|
|||
"for elaboration:" << pins[idx] << endl;
|
||||
continue;
|
||||
}
|
||||
NetNet*sig = expr_tmp->synthesize(des, scope);
|
||||
NetNet*sig = expr_tmp->synthesize(des, scope, expr_tmp);
|
||||
ivl_assert(*this, sig);
|
||||
sig->set_line(*this);
|
||||
|
||||
|
|
@ -2984,7 +2984,7 @@ NetProc* PEventStatement::elaborate_st(Design*des, NetScope*scope,
|
|||
continue;
|
||||
}
|
||||
|
||||
NetNet*expr = tmp->synthesize(des, scope);
|
||||
NetNet*expr = tmp->synthesize(des, scope, tmp);
|
||||
expr->set_line(*this);
|
||||
if (expr == 0) {
|
||||
expr_[idx]->dump(cerr);
|
||||
|
|
@ -3754,7 +3754,7 @@ void PSpecPath::elaborate(Design*des, NetScope*scope) const
|
|||
// FIXME: Look for constant expressions here?
|
||||
|
||||
// Get a net form.
|
||||
condit_sig = tmp->synthesize(des, scope);
|
||||
condit_sig = tmp->synthesize(des, scope, tmp);
|
||||
ivl_assert(*condition, condit_sig);
|
||||
}
|
||||
|
||||
|
|
|
|||
155
expr_synth.cc
155
expr_synth.cc
|
|
@ -31,13 +31,13 @@ static NetNet* convert_to_real_const(Design*des, NetScope*scope, NetEConst*expr)
|
|||
{
|
||||
verireal vrl(expr->value().as_double());
|
||||
NetECReal rlval(vrl);
|
||||
NetNet* sig = rlval.synthesize(des, scope);
|
||||
NetNet* sig = rlval.synthesize(des, scope, 0);
|
||||
|
||||
return sig;
|
||||
}
|
||||
|
||||
/* Note that lsig, rsig and real_args are references. */
|
||||
static bool process_binary_args(Design*des, NetScope*scope,
|
||||
static bool process_binary_args(Design*des, NetScope*scope, NetExpr*root,
|
||||
NetExpr*left, NetExpr*right,
|
||||
NetNet*&lsig, NetNet*&rsig, bool&real_args,
|
||||
NetExpr*obj)
|
||||
|
|
@ -49,27 +49,27 @@ static bool process_binary_args(Design*des, NetScope*scope,
|
|||
/* Convert the arguments to real. Handle the special
|
||||
cases of constants, which can be converted more directly. */
|
||||
if (left->expr_type() == IVL_VT_REAL) {
|
||||
lsig = left->synthesize(des, scope);
|
||||
lsig = left->synthesize(des, scope, root);
|
||||
} else if (NetEConst*tmpc = dynamic_cast<NetEConst*> (left)) {
|
||||
lsig = convert_to_real_const(des, scope, tmpc);
|
||||
} else {
|
||||
NetNet*tmp = left->synthesize(des, scope);
|
||||
NetNet*tmp = left->synthesize(des, scope, root);
|
||||
lsig = cast_to_real(des, scope, tmp);
|
||||
}
|
||||
|
||||
if (right->expr_type() == IVL_VT_REAL) {
|
||||
rsig = right->synthesize(des, scope);
|
||||
rsig = right->synthesize(des, scope, root);
|
||||
} else if (NetEConst*tmpc = dynamic_cast<NetEConst*> (right)) {
|
||||
rsig = convert_to_real_const(des, scope, tmpc);
|
||||
} else {
|
||||
NetNet*tmp = right->synthesize(des, scope);
|
||||
NetNet*tmp = right->synthesize(des, scope, root);
|
||||
rsig = cast_to_real(des, scope, tmp);
|
||||
}
|
||||
|
||||
} else {
|
||||
real_args = false;
|
||||
lsig = left->synthesize(des, scope);
|
||||
rsig = right->synthesize(des, scope);
|
||||
lsig = left->synthesize(des, scope, root);
|
||||
rsig = right->synthesize(des, scope, root);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -77,7 +77,7 @@ static bool process_binary_args(Design*des, NetScope*scope,
|
|||
else return false;
|
||||
}
|
||||
|
||||
NetNet* NetExpr::synthesize(Design*des, NetScope*scope)
|
||||
NetNet* NetExpr::synthesize(Design*des, NetScope*scope, NetExpr*root)
|
||||
{
|
||||
cerr << get_fileline() << ": internal error: cannot synthesize expression: "
|
||||
<< *this << endl;
|
||||
|
|
@ -88,13 +88,13 @@ NetNet* NetExpr::synthesize(Design*des, NetScope*scope)
|
|||
/*
|
||||
* Make an LPM_ADD_SUB device from addition operators.
|
||||
*/
|
||||
NetNet* NetEBAdd::synthesize(Design*des, NetScope*scope)
|
||||
NetNet* NetEBAdd::synthesize(Design*des, NetScope*scope, NetExpr*root)
|
||||
{
|
||||
ivl_assert(*this, (op()=='+') || (op()=='-'));
|
||||
|
||||
NetNet *lsig=0, *rsig=0;
|
||||
bool real_args=false;
|
||||
if (process_binary_args(des, scope, left_, right_, lsig, rsig,
|
||||
if (process_binary_args(des, scope, root, left_, right_, lsig, rsig,
|
||||
real_args, this)) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -148,10 +148,10 @@ NetNet* NetEBAdd::synthesize(Design*des, NetScope*scope)
|
|||
* signals, then just connect a single gate to each bit of the vector
|
||||
* of the expression.
|
||||
*/
|
||||
NetNet* NetEBBits::synthesize(Design*des, NetScope*scope)
|
||||
NetNet* NetEBBits::synthesize(Design*des, NetScope*scope, NetExpr*root)
|
||||
{
|
||||
NetNet*lsig = left_->synthesize(des, scope);
|
||||
NetNet*rsig = right_->synthesize(des, scope);
|
||||
NetNet*lsig = left_->synthesize(des, scope, root);
|
||||
NetNet*rsig = right_->synthesize(des, scope, root);
|
||||
|
||||
if (lsig == 0 || rsig == 0) return 0;
|
||||
|
||||
|
|
@ -212,13 +212,13 @@ NetNet* NetEBBits::synthesize(Design*des, NetScope*scope)
|
|||
return osig;
|
||||
}
|
||||
|
||||
NetNet* NetEBComp::synthesize(Design*des, NetScope*scope)
|
||||
NetNet* NetEBComp::synthesize(Design*des, NetScope*scope, NetExpr*root)
|
||||
{
|
||||
|
||||
NetNet *lsig=0, *rsig=0;
|
||||
unsigned width;
|
||||
bool real_args=false;
|
||||
if (process_binary_args(des, scope, left_, right_, lsig, rsig,
|
||||
if (process_binary_args(des, scope, root, left_, right_, lsig, rsig,
|
||||
real_args, this)) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -362,12 +362,12 @@ NetNet* NetEBComp::synthesize(Design*des, NetScope*scope)
|
|||
return osig;
|
||||
}
|
||||
|
||||
NetNet* NetEBPow::synthesize(Design*des, NetScope*scope)
|
||||
NetNet* NetEBPow::synthesize(Design*des, NetScope*scope, NetExpr*root)
|
||||
{
|
||||
NetNet *lsig=0, *rsig=0;
|
||||
unsigned width;
|
||||
bool real_args=false;
|
||||
if (process_binary_args(des, scope, left_, right_, lsig, rsig,
|
||||
if (process_binary_args(des, scope, root, left_, right_, lsig, rsig,
|
||||
real_args, this)) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -397,12 +397,12 @@ NetNet* NetEBPow::synthesize(Design*des, NetScope*scope)
|
|||
return osig;
|
||||
}
|
||||
|
||||
NetNet* NetEBMult::synthesize(Design*des, NetScope*scope)
|
||||
NetNet* NetEBMult::synthesize(Design*des, NetScope*scope, NetExpr*root)
|
||||
{
|
||||
NetNet *lsig=0, *rsig=0;
|
||||
unsigned width;
|
||||
bool real_args=false;
|
||||
if (process_binary_args(des, scope, left_, right_, lsig, rsig,
|
||||
if (process_binary_args(des, scope, root, left_, right_, lsig, rsig,
|
||||
real_args, this)) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -433,12 +433,12 @@ NetNet* NetEBMult::synthesize(Design*des, NetScope*scope)
|
|||
return osig;
|
||||
}
|
||||
|
||||
NetNet* NetEBDiv::synthesize(Design*des, NetScope*scope)
|
||||
NetNet* NetEBDiv::synthesize(Design*des, NetScope*scope, NetExpr*root)
|
||||
{
|
||||
NetNet *lsig=0, *rsig=0;
|
||||
unsigned width;
|
||||
bool real_args=false;
|
||||
if (process_binary_args(des, scope, left_, right_, lsig, rsig,
|
||||
if (process_binary_args(des, scope, root, left_, right_, lsig, rsig,
|
||||
real_args, this)) {
|
||||
return 0;
|
||||
}
|
||||
|
|
@ -507,10 +507,10 @@ NetNet* NetEBDiv::synthesize(Design*des, NetScope*scope)
|
|||
return osig;
|
||||
}
|
||||
|
||||
NetNet* NetEBLogic::synthesize(Design*des, NetScope*scope)
|
||||
NetNet* NetEBLogic::synthesize(Design*des, NetScope*scope, NetExpr*root)
|
||||
{
|
||||
NetNet*lsig = left_->synthesize(des, scope);
|
||||
NetNet*rsig = right_->synthesize(des, scope);
|
||||
NetNet*lsig = left_->synthesize(des, scope, root);
|
||||
NetNet*rsig = right_->synthesize(des, scope, root);
|
||||
|
||||
if (lsig == 0 || rsig == 0) return 0;
|
||||
|
||||
|
|
@ -584,11 +584,11 @@ NetNet* NetEBLogic::synthesize(Design*des, NetScope*scope)
|
|||
return osig;
|
||||
}
|
||||
|
||||
NetNet* NetEBShift::synthesize(Design*des, NetScope*scope)
|
||||
NetNet* NetEBShift::synthesize(Design*des, NetScope*scope, NetExpr*root)
|
||||
{
|
||||
eval_expr(right_);
|
||||
|
||||
NetNet*lsig = left_->synthesize(des, scope);
|
||||
NetNet*lsig = left_->synthesize(des, scope, root);
|
||||
|
||||
if (lsig == 0) return 0;
|
||||
|
||||
|
|
@ -690,7 +690,7 @@ NetNet* NetEBShift::synthesize(Design*des, NetScope*scope)
|
|||
return osig;
|
||||
}
|
||||
|
||||
NetNet*rsig = right_->synthesize(des, scope);
|
||||
NetNet*rsig = right_->synthesize(des, scope, root);
|
||||
|
||||
if (rsig == 0) return 0;
|
||||
|
||||
|
|
@ -716,13 +716,13 @@ NetNet* NetEBShift::synthesize(Design*des, NetScope*scope)
|
|||
return osig;
|
||||
}
|
||||
|
||||
NetNet* NetEConcat::synthesize(Design*des, NetScope*scope)
|
||||
NetNet* NetEConcat::synthesize(Design*des, NetScope*scope, NetExpr*root)
|
||||
{
|
||||
/* First, synthesize the operands. */
|
||||
NetNet**tmp = new NetNet*[parms_.count()];
|
||||
bool flag = true;
|
||||
for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) {
|
||||
tmp[idx] = parms_[idx]->synthesize(des, scope);
|
||||
tmp[idx] = parms_[idx]->synthesize(des, scope, root);
|
||||
if (tmp[idx] == 0)
|
||||
flag = false;
|
||||
}
|
||||
|
|
@ -769,7 +769,7 @@ NetNet* NetEConcat::synthesize(Design*des, NetScope*scope)
|
|||
return osig;
|
||||
}
|
||||
|
||||
NetNet* NetEConst::synthesize(Design*des, NetScope*scope)
|
||||
NetNet* NetEConst::synthesize(Design*des, NetScope*scope, NetExpr*)
|
||||
{
|
||||
perm_string path = scope->local_symbol();
|
||||
unsigned width=expr_width();
|
||||
|
|
@ -791,7 +791,7 @@ NetNet* NetEConst::synthesize(Design*des, NetScope*scope)
|
|||
/*
|
||||
* Create a NetLiteral object to represent real valued constants.
|
||||
*/
|
||||
NetNet* NetECReal::synthesize(Design*des, NetScope*scope)
|
||||
NetNet* NetECReal::synthesize(Design*des, NetScope*scope, NetExpr*)
|
||||
{
|
||||
perm_string path = scope->local_symbol();
|
||||
|
||||
|
|
@ -813,9 +813,9 @@ NetNet* NetECReal::synthesize(Design*des, NetScope*scope)
|
|||
* The bitwise unary logic operator (there is only one) is turned
|
||||
* into discrete gates just as easily as the binary ones above.
|
||||
*/
|
||||
NetNet* NetEUBits::synthesize(Design*des, NetScope*scope)
|
||||
NetNet* NetEUBits::synthesize(Design*des, NetScope*scope, NetExpr*root)
|
||||
{
|
||||
NetNet*isig = expr_->synthesize(des, scope);
|
||||
NetNet*isig = expr_->synthesize(des, scope, root);
|
||||
|
||||
if (isig == 0) return 0;
|
||||
|
||||
|
|
@ -852,19 +852,19 @@ NetNet* NetEUBits::synthesize(Design*des, NetScope*scope)
|
|||
return osig;
|
||||
}
|
||||
|
||||
NetNet* NetEUnary::synthesize(Design*des, NetScope*scope)
|
||||
NetNet* NetEUnary::synthesize(Design*des, NetScope*scope, NetExpr*root)
|
||||
{
|
||||
if (op_ == '+')
|
||||
return expr_->synthesize(des, scope);
|
||||
return expr_->synthesize(des, scope, root);
|
||||
|
||||
if (op_ == '-') {
|
||||
NetNet*sig = expr_->synthesize(des, scope);
|
||||
NetNet*sig = expr_->synthesize(des, scope, root);
|
||||
sig = sub_net_from(des, scope, 0, sig);
|
||||
return sig;
|
||||
}
|
||||
|
||||
if (op_ == 'm') {
|
||||
NetNet*sub = expr_->synthesize(des, scope);
|
||||
NetNet*sub = expr_->synthesize(des, scope, root);
|
||||
if (expr_->has_sign() == false)
|
||||
return sub;
|
||||
|
||||
|
|
@ -886,12 +886,12 @@ NetNet* NetEUnary::synthesize(Design*des, NetScope*scope)
|
|||
cerr << get_fileline() << ": iternal error: "
|
||||
<< "NetEUnary::synthesize cannot handle op_=" << op_ << endl;
|
||||
des->errors += 1;
|
||||
return expr_->synthesize(des, scope);
|
||||
return expr_->synthesize(des, scope, root);
|
||||
}
|
||||
|
||||
NetNet* NetEUReduce::synthesize(Design*des, NetScope*scope)
|
||||
NetNet* NetEUReduce::synthesize(Design*des, NetScope*scope, NetExpr*root)
|
||||
{
|
||||
NetNet*isig = expr_->synthesize(des, scope);
|
||||
NetNet*isig = expr_->synthesize(des, scope, root);
|
||||
|
||||
if (isig == 0) return 0;
|
||||
|
||||
|
|
@ -961,10 +961,10 @@ NetNet* NetEUReduce::synthesize(Design*des, NetScope*scope)
|
|||
* - Expression elaboration already converted the offset expression into
|
||||
* canonical form, so we don't have to worry about that here.
|
||||
*/
|
||||
NetNet* NetESelect::synthesize(Design *des, NetScope*scope)
|
||||
NetNet* NetESelect::synthesize(Design *des, NetScope*scope, NetExpr*root)
|
||||
{
|
||||
|
||||
NetNet*sub = expr_->synthesize(des, scope);
|
||||
NetNet*sub = expr_->synthesize(des, scope, root);
|
||||
|
||||
if (sub == 0) return 0;
|
||||
|
||||
|
|
@ -1065,7 +1065,7 @@ NetNet* NetESelect::synthesize(Design *des, NetScope*scope)
|
|||
// actual part/bit select. Generate a NetPartSelect object to
|
||||
// do the work, and replace "sub" with the selected output.
|
||||
if (base_ != 0) {
|
||||
off = base_->synthesize(des, scope);
|
||||
off = base_->synthesize(des, scope, root);
|
||||
|
||||
NetPartSelect*sel = new NetPartSelect(sub, off, expr_width());
|
||||
sel->set_line(*this);
|
||||
|
|
@ -1146,11 +1146,11 @@ NetNet* NetESelect::synthesize(Design *des, NetScope*scope)
|
|||
* expressions to the B and A inputs. This way, when the select input
|
||||
* is one, the B input, which is the true expression, is selected.
|
||||
*/
|
||||
NetNet* NetETernary::synthesize(Design *des, NetScope*scope)
|
||||
NetNet* NetETernary::synthesize(Design *des, NetScope*scope, NetExpr*root)
|
||||
{
|
||||
NetNet*csig = cond_->synthesize(des, scope),
|
||||
*tsig = true_val_->synthesize(des, scope),
|
||||
*fsig = false_val_->synthesize(des, scope);
|
||||
NetNet*csig = cond_->synthesize(des, scope, root),
|
||||
*tsig = true_val_->synthesize(des, scope, root),
|
||||
*fsig = false_val_->synthesize(des, scope, root);
|
||||
|
||||
if (csig == 0 || tsig == 0 || fsig == 0) return 0;
|
||||
|
||||
|
|
@ -1209,7 +1209,7 @@ NetNet* NetETernary::synthesize(Design *des, NetScope*scope)
|
|||
* a bit more work needs to be done. Return a temporary that represents
|
||||
* the selected word.
|
||||
*/
|
||||
NetNet* NetESignal::synthesize(Design*des, NetScope*scope)
|
||||
NetNet* NetESignal::synthesize(Design*des, NetScope*scope, NetExpr*root)
|
||||
{
|
||||
if (word_ == 0)
|
||||
return net_;
|
||||
|
|
@ -1236,7 +1236,7 @@ NetNet* NetESignal::synthesize(Design*des, NetScope*scope)
|
|||
mux->set_line(*this);
|
||||
des->add_node(mux);
|
||||
|
||||
NetNet*index_net = word_->synthesize(des, scope);
|
||||
NetNet*index_net = word_->synthesize(des, scope, root);
|
||||
connect(mux->pin_Address(), index_net->pin(0));
|
||||
|
||||
connect(tmp->pin(0), mux->pin_Result());
|
||||
|
|
@ -1244,7 +1244,35 @@ NetNet* NetESignal::synthesize(Design*des, NetScope*scope)
|
|||
return tmp;
|
||||
}
|
||||
|
||||
NetNet* NetESFunc::synthesize(Design*des, NetScope*scope)
|
||||
static NetEvWait* make_func_trigger(Design*des, NetScope*scope, NetExpr*root)
|
||||
{
|
||||
NetEvWait*trigger = 0;
|
||||
|
||||
NexusSet*nset = root->nex_input(false);
|
||||
if (nset && (nset->count() > 0)) {
|
||||
NetEvent*ev = new NetEvent(scope->local_symbol());
|
||||
ev->set_line(*root);
|
||||
|
||||
NetEvProbe*pr = new NetEvProbe(scope, scope->local_symbol(),
|
||||
ev, NetEvProbe::ANYEDGE,
|
||||
nset->count());
|
||||
for (unsigned idx = 0 ; idx < nset->count() ; idx += 1)
|
||||
connect(nset[0][idx], pr->pin(idx));
|
||||
|
||||
des->add_node(pr);
|
||||
|
||||
scope->add_event(ev);
|
||||
|
||||
trigger = new NetEvWait(0);
|
||||
trigger->set_line(*root);
|
||||
trigger->add_event(ev);
|
||||
}
|
||||
delete nset;
|
||||
|
||||
return trigger;
|
||||
}
|
||||
|
||||
NetNet* NetESFunc::synthesize(Design*des, NetScope*scope, NetExpr*root)
|
||||
{
|
||||
|
||||
const struct sfunc_return_type*def = lookup_sys_func(name_);
|
||||
|
|
@ -1264,8 +1292,13 @@ NetNet* NetESFunc::synthesize(Design*des, NetScope*scope)
|
|||
<< name_ << " returns " << def->type << endl;
|
||||
}
|
||||
|
||||
NetEvWait*trigger = 0;
|
||||
if (nparms_ == 0) {
|
||||
trigger = make_func_trigger(des, scope, root);
|
||||
}
|
||||
|
||||
NetSysFunc*net = new NetSysFunc(scope, scope->local_symbol(),
|
||||
def, 1+nparms_);
|
||||
def, 1+nparms_, trigger);
|
||||
net->set_line(*this);
|
||||
des->add_node(net);
|
||||
|
||||
|
|
@ -1280,7 +1313,7 @@ NetNet* NetESFunc::synthesize(Design*des, NetScope*scope)
|
|||
|
||||
unsigned errors = 0;
|
||||
for (unsigned idx = 0 ; idx < nparms_ ; idx += 1) {
|
||||
NetNet*tmp = parms_[idx]->synthesize(des, scope);
|
||||
NetNet*tmp = parms_[idx]->synthesize(des, scope, root);
|
||||
if (tmp == 0) {
|
||||
cerr << get_fileline() << ": error: Unable to elaborate "
|
||||
<< "argument " << idx << " of call to " << name_ <<
|
||||
|
|
@ -1298,14 +1331,14 @@ NetNet* NetESFunc::synthesize(Design*des, NetScope*scope)
|
|||
return osig;
|
||||
}
|
||||
|
||||
NetNet* NetEUFunc::synthesize(Design*des, NetScope*scope)
|
||||
NetNet* NetEUFunc::synthesize(Design*des, NetScope*scope, NetExpr*root)
|
||||
{
|
||||
svector<NetNet*> eparms (parms_.count());
|
||||
|
||||
/* Synthesize the arguments. */
|
||||
bool errors = false;
|
||||
for (unsigned idx = 0; idx < eparms.count(); idx += 1) {
|
||||
NetNet*tmp = parms_[idx]->synthesize(des, scope);
|
||||
NetNet*tmp = parms_[idx]->synthesize(des, scope, root);
|
||||
if (tmp == 0) {
|
||||
cerr << get_fileline() << ": error: Unable to synthesize "
|
||||
"port " << idx << " of call to "
|
||||
|
|
@ -1318,7 +1351,17 @@ NetNet* NetEUFunc::synthesize(Design*des, NetScope*scope)
|
|||
}
|
||||
if (errors) return 0;
|
||||
|
||||
NetUserFunc*net = new NetUserFunc(scope_, scope_->local_symbol(), func_);
|
||||
NetEvWait*trigger = 0;
|
||||
if (gn_strict_ca_eval_flag) {
|
||||
/* Ideally we would only do this for functions that have hidden
|
||||
dependencies or side effects. Once constant functions are
|
||||
implemented, we may be able to reuse some code to achieve
|
||||
this. */
|
||||
trigger = make_func_trigger(des, scope, root);
|
||||
}
|
||||
|
||||
NetUserFunc*net = new NetUserFunc(scope_, scope_->local_symbol(), func_,
|
||||
trigger);
|
||||
net->set_line(*this);
|
||||
des->add_node(net);
|
||||
|
||||
|
|
|
|||
15
ivl_target.h
15
ivl_target.h
|
|
@ -1024,6 +1024,10 @@ extern unsigned ivl_lpm_lineno(ivl_lpm_t net);
|
|||
* often the number of inputs per out, i.e., the number of inputs
|
||||
* per bit for a MUX.
|
||||
*
|
||||
* ivl_lpm_trigger
|
||||
* SFUNC and UFUNC devices may have a trigger that forces the
|
||||
* function output to be re-evaluated.
|
||||
*
|
||||
* SEMANTIC NOTES
|
||||
*
|
||||
* - Concatenation (IVL_LPM_CONCAT)
|
||||
|
|
@ -1186,6 +1190,11 @@ extern unsigned ivl_lpm_lineno(ivl_lpm_t net);
|
|||
* function (i.e. "$display") that was found in the source code. The
|
||||
* compiler does little checking of that name.
|
||||
*
|
||||
* The ivl_lpm_trigger function retrieves the trigger event that
|
||||
* indicates when the system function needs to be re-evaluated. If
|
||||
* there is no trigger event, the system function only needs to be
|
||||
* re-evaluated when a change is detected on its input ports.
|
||||
*
|
||||
* - User Function Call (IVL_LPM_UFUNC)
|
||||
* This device is special as it represents a call to a user defined
|
||||
* function (behavioral code) within a netlist. The inputs to the
|
||||
|
|
@ -1204,6 +1213,11 @@ extern unsigned ivl_lpm_lineno(ivl_lpm_t net);
|
|||
* ports. The ivl_lpm_size function returns the number of inputs for
|
||||
* the device, and the ivl_lpm_data() function index argument selects
|
||||
* the port to retrieve. Each port is sized independently.
|
||||
*
|
||||
* The ivl_lpm_trigger function retrieves the trigger event that
|
||||
* indicates when the user function needs to be re-evaluated. If
|
||||
* there is no trigger event, the user function only needs to be
|
||||
* re-evaluated when a change is detected on its input ports.
|
||||
*/
|
||||
|
||||
extern const char* ivl_lpm_name(ivl_lpm_t net); /* (Obsolete) */
|
||||
|
|
@ -1213,6 +1227,7 @@ extern ivl_scope_t ivl_lpm_scope(ivl_lpm_t net);
|
|||
extern int ivl_lpm_signed(ivl_lpm_t net);
|
||||
extern ivl_lpm_type_t ivl_lpm_type(ivl_lpm_t net);
|
||||
extern unsigned ivl_lpm_width(ivl_lpm_t net);
|
||||
extern ivl_event_t ivl_lpm_trigger(ivl_lpm_t net);
|
||||
|
||||
/* IVL_LPM_FF */
|
||||
extern ivl_nexus_t ivl_lpm_async_clr(ivl_lpm_t net);
|
||||
|
|
|
|||
10
main.cc
10
main.cc
|
|
@ -89,6 +89,7 @@ bool gn_icarus_misc_flag = true;
|
|||
bool gn_cadence_types_flag = true;
|
||||
bool gn_specify_blocks_flag = true;
|
||||
bool gn_io_range_error_flag = true;
|
||||
bool gn_strict_ca_eval_flag = false;
|
||||
bool gn_verilog_ams_flag = false;
|
||||
|
||||
map<string,const char*> flags;
|
||||
|
|
@ -125,7 +126,6 @@ bool debug_elaborate = false;
|
|||
bool debug_elab_pexpr = false;
|
||||
bool debug_synth2 = false;
|
||||
bool debug_optimizer = false;
|
||||
bool debug_automatic = false;
|
||||
|
||||
/*
|
||||
* Verbose messages enabled.
|
||||
|
|
@ -246,6 +246,12 @@ static void process_generation_flag(const char*gen)
|
|||
} else if (strcmp(gen,"no-io-range-error") == 0) {
|
||||
gn_io_range_error_flag = false;
|
||||
|
||||
} else if (strcmp(gen,"strict-ca-eval") == 0) {
|
||||
gn_strict_ca_eval_flag = true;
|
||||
|
||||
} else if (strcmp(gen,"no-strict-ca-eval") == 0) {
|
||||
gn_strict_ca_eval_flag = false;
|
||||
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
|
@ -397,8 +403,6 @@ static void read_iconfig_file(const char*ipath)
|
|||
} else if (strcmp(cp,"optimizer") == 0) {
|
||||
debug_optimizer = true;
|
||||
cerr << "debug: Enable optimizer debug" << endl;
|
||||
} else if (strcmp(cp,"automatic") == 0) {
|
||||
debug_automatic = true;
|
||||
} else {
|
||||
}
|
||||
|
||||
|
|
|
|||
11
net_func.cc
11
net_func.cc
|
|
@ -29,9 +29,9 @@
|
|||
* which accounts for all the inputs, plus one for the phantom output
|
||||
* that is the result.
|
||||
*/
|
||||
NetUserFunc::NetUserFunc(NetScope*s, perm_string n, NetScope*d)
|
||||
NetUserFunc::NetUserFunc(NetScope*s, perm_string n, NetScope*d, NetEvWait*trigger)
|
||||
: NetNode(s, n, d->func_def()->port_count()+1),
|
||||
def_(d)
|
||||
def_(d), trigger_(trigger)
|
||||
{
|
||||
pin(0).set_dir(Link::OUTPUT);
|
||||
|
||||
|
|
@ -128,11 +128,9 @@ bool PECallFunction::check_call_matches_definition_(Design*des, NetScope*dscope)
|
|||
|
||||
NetSysFunc::NetSysFunc(NetScope*s, perm_string n,
|
||||
const struct sfunc_return_type*def,
|
||||
unsigned ports)
|
||||
: NetNode(s, n, ports)
|
||||
unsigned ports, NetEvWait*trigger)
|
||||
: NetNode(s, n, ports), def_(def), trigger_(trigger)
|
||||
{
|
||||
def_ = def;
|
||||
|
||||
pin(0).set_dir(Link::OUTPUT); // Q
|
||||
|
||||
for (unsigned idx = 1 ; idx < pin_count() ; idx += 1) {
|
||||
|
|
@ -161,4 +159,3 @@ unsigned NetSysFunc::vector_width() const
|
|||
{
|
||||
return def_->wid;
|
||||
}
|
||||
|
||||
|
|
|
|||
52
netlist.h
52
netlist.h
|
|
@ -1439,7 +1439,7 @@ class NetReplicate : public NetNode {
|
|||
class NetUserFunc : public NetNode {
|
||||
|
||||
public:
|
||||
NetUserFunc(NetScope*s, perm_string n, NetScope*def);
|
||||
NetUserFunc(NetScope*s, perm_string n, NetScope*def, NetEvWait*trigger);
|
||||
~NetUserFunc();
|
||||
|
||||
ivl_variable_type_t data_type(unsigned port) const;
|
||||
|
|
@ -1447,11 +1447,14 @@ class NetUserFunc : public NetNode {
|
|||
|
||||
const NetScope* def() const;
|
||||
|
||||
const NetEvWait* trigger() const { return trigger_; }
|
||||
|
||||
virtual void dump_node(ostream&, unsigned ind) const;
|
||||
virtual bool emit_node(struct target_t*) const;
|
||||
|
||||
private:
|
||||
NetScope*def_;
|
||||
NetEvWait*trigger_;
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -1463,18 +1466,21 @@ class NetSysFunc : public NetNode {
|
|||
public:
|
||||
NetSysFunc(NetScope*s, perm_string n,
|
||||
const struct sfunc_return_type*def,
|
||||
unsigned ports);
|
||||
unsigned ports, NetEvWait*trigger);
|
||||
~NetSysFunc();
|
||||
|
||||
ivl_variable_type_t data_type() const;
|
||||
unsigned vector_width() const;
|
||||
const char* func_name() const;
|
||||
|
||||
const NetEvWait* trigger() const { return trigger_; }
|
||||
|
||||
virtual void dump_node(ostream&, unsigned ind) const;
|
||||
virtual bool emit_node(struct target_t*) const;
|
||||
|
||||
private:
|
||||
const struct sfunc_return_type*def_;
|
||||
NetEvWait*trigger_;
|
||||
};
|
||||
|
||||
class NetTran : public NetNode, public IslandBranch {
|
||||
|
|
@ -1598,12 +1604,14 @@ class NetExpr : public LineInfo {
|
|||
//
|
||||
// des, scope: The context where this work is done
|
||||
//
|
||||
// root: The root expression of which this expression is a part.
|
||||
//
|
||||
// rise/fall/decay: Attach these delays to the driver for the
|
||||
// expression output.
|
||||
//
|
||||
// drive0/drive1: Attach these strengths tp the driver for
|
||||
// the expression output.
|
||||
virtual NetNet*synthesize(Design*des, NetScope*scope);
|
||||
virtual NetNet*synthesize(Design*des, NetScope*scope, NetExpr*root);
|
||||
|
||||
|
||||
protected:
|
||||
|
|
@ -1641,7 +1649,7 @@ class NetEConst : public NetExpr {
|
|||
virtual void dump(ostream&) const;
|
||||
|
||||
virtual NetEConst* dup_expr() const;
|
||||
virtual NetNet*synthesize(Design*, NetScope*scope);
|
||||
virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*);
|
||||
virtual NexusSet* nex_input(bool rem_out = true);
|
||||
|
||||
private:
|
||||
|
|
@ -1694,7 +1702,7 @@ class NetECReal : public NetExpr {
|
|||
virtual void dump(ostream&) const;
|
||||
|
||||
virtual NetECReal* dup_expr() const;
|
||||
virtual NetNet*synthesize(Design*, NetScope*scope);
|
||||
virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*);
|
||||
virtual NexusSet* nex_input(bool rem_out = true);
|
||||
|
||||
private:
|
||||
|
|
@ -2999,7 +3007,7 @@ class NetEUFunc : public NetExpr {
|
|||
virtual void expr_scan(struct expr_scan_t*) const;
|
||||
virtual NetEUFunc*dup_expr() const;
|
||||
virtual NexusSet* nex_input(bool rem_out = true);
|
||||
virtual NetNet* synthesize(Design*des, NetScope*scope);
|
||||
virtual NetNet* synthesize(Design*des, NetScope*scope, NetExpr*root);
|
||||
|
||||
private:
|
||||
NetScope*scope_;
|
||||
|
|
@ -3242,7 +3250,7 @@ class NetEBAdd : public NetEBinary {
|
|||
virtual bool set_width(unsigned w, bool last_chance);
|
||||
virtual NetEBAdd* dup_expr() const;
|
||||
virtual NetExpr* eval_tree(int prune_to_width = -1);
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope);
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root);
|
||||
|
||||
private:
|
||||
NetECReal* eval_tree_real_();
|
||||
|
|
@ -3264,7 +3272,7 @@ class NetEBDiv : public NetEBinary {
|
|||
virtual bool set_width(unsigned w, bool last_chance);
|
||||
virtual NetEBDiv* dup_expr() const;
|
||||
virtual NetExpr* eval_tree(int prune_to_width = -1);
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope);
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -3291,7 +3299,7 @@ class NetEBBits : public NetEBinary {
|
|||
virtual NetEBBits* dup_expr() const;
|
||||
virtual NetEConst* eval_tree(int prune_to_width = -1);
|
||||
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope);
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root);
|
||||
};
|
||||
|
||||
/*
|
||||
|
|
@ -3322,7 +3330,7 @@ class NetEBComp : public NetEBinary {
|
|||
virtual NetEBComp* dup_expr() const;
|
||||
virtual NetEConst* eval_tree(int prune_to_width = -1);
|
||||
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope);
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root);
|
||||
|
||||
private:
|
||||
NetEConst* must_be_leeq_(NetExpr*le, const verinum&rv, bool eq_flag);
|
||||
|
|
@ -3354,7 +3362,7 @@ class NetEBLogic : public NetEBinary {
|
|||
virtual bool set_width(unsigned w, bool last_chance =false);
|
||||
virtual NetEBLogic* dup_expr() const;
|
||||
virtual NetEConst* eval_tree(int prune_to_width = -1);
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope);
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root);
|
||||
|
||||
private:
|
||||
};
|
||||
|
|
@ -3392,7 +3400,7 @@ class NetEBMult : public NetEBinary {
|
|||
virtual bool set_width(unsigned w, bool last_chance);
|
||||
virtual NetEBMult* dup_expr() const;
|
||||
virtual NetExpr* eval_tree(int prune_to_width = -1);
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope);
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root);
|
||||
|
||||
private:
|
||||
|
||||
|
|
@ -3414,7 +3422,7 @@ class NetEBPow : public NetEBinary {
|
|||
virtual bool set_width(unsigned w, bool last_chance);
|
||||
virtual NetEBPow* dup_expr() const;
|
||||
virtual NetExpr* eval_tree(int prune_to_width = -1);
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope);
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root);
|
||||
|
||||
private:
|
||||
|
||||
|
|
@ -3446,7 +3454,7 @@ class NetEBShift : public NetEBinary {
|
|||
virtual NetEBShift* dup_expr() const;
|
||||
virtual NetEConst* eval_tree(int prune_to_width = -1);
|
||||
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope);
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root);
|
||||
|
||||
private:
|
||||
};
|
||||
|
|
@ -3480,7 +3488,7 @@ class NetEConcat : public NetExpr {
|
|||
virtual bool set_width(unsigned w, bool last_chance =false);
|
||||
virtual NetEConcat* dup_expr() const;
|
||||
virtual NetEConst* eval_tree(int prune_to_width = -1);
|
||||
virtual NetNet*synthesize(Design*, NetScope*scope);
|
||||
virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*root);
|
||||
virtual void expr_scan(struct expr_scan_t*) const;
|
||||
virtual void dump(ostream&) const;
|
||||
|
||||
|
|
@ -3557,7 +3565,7 @@ class NetESelect : public NetExpr {
|
|||
virtual void expr_scan(struct expr_scan_t*) const;
|
||||
virtual NetEConst* eval_tree(int prune_to_width = -1);
|
||||
virtual NetESelect* dup_expr() const;
|
||||
virtual NetNet*synthesize(Design*des, NetScope*scope);
|
||||
virtual NetNet*synthesize(Design*des, NetScope*scope, NetExpr*root);
|
||||
virtual void dump(ostream&) const;
|
||||
|
||||
private:
|
||||
|
|
@ -3637,7 +3645,7 @@ class NetESFunc : public NetExpr {
|
|||
|
||||
virtual void expr_scan(struct expr_scan_t*) const;
|
||||
virtual NetESFunc*dup_expr() const;
|
||||
virtual NetNet*synthesize(Design*, NetScope*scope);
|
||||
virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*root);
|
||||
|
||||
private:
|
||||
const char* name_;
|
||||
|
|
@ -3674,7 +3682,7 @@ class NetETernary : public NetExpr {
|
|||
virtual NexusSet* nex_input(bool rem_out = true);
|
||||
virtual void expr_scan(struct expr_scan_t*) const;
|
||||
virtual void dump(ostream&) const;
|
||||
virtual NetNet*synthesize(Design*, NetScope*scope);
|
||||
virtual NetNet*synthesize(Design*, NetScope*scope, NetExpr*root);
|
||||
|
||||
public:
|
||||
static bool test_operand_compat(ivl_variable_type_t tru, ivl_variable_type_t fal);
|
||||
|
|
@ -3714,7 +3722,7 @@ class NetEUnary : public NetExpr {
|
|||
|
||||
virtual NetEUnary* dup_expr() const;
|
||||
virtual NetExpr* eval_tree(int prune_to_width = -1);
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope);
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root);
|
||||
|
||||
virtual ivl_variable_type_t expr_type() const;
|
||||
virtual NexusSet* nex_input(bool rem_out = true);
|
||||
|
|
@ -3735,7 +3743,7 @@ class NetEUBits : public NetEUnary {
|
|||
NetEUBits(char op, NetExpr*ex);
|
||||
~NetEUBits();
|
||||
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope);
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root);
|
||||
|
||||
virtual NetEUBits* dup_expr() const;
|
||||
virtual NetExpr* eval_tree(int prune_to_width = -1);
|
||||
|
|
@ -3749,7 +3757,7 @@ class NetEUReduce : public NetEUnary {
|
|||
~NetEUReduce();
|
||||
|
||||
virtual bool set_width(unsigned w, bool last_chance);
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope);
|
||||
virtual NetNet* synthesize(Design*, NetScope*scope, NetExpr*root);
|
||||
virtual NetEUReduce* dup_expr() const;
|
||||
virtual NetEConst* eval_tree(int prune_to_width = -1);
|
||||
virtual ivl_variable_type_t expr_type() const;
|
||||
|
|
@ -3776,7 +3784,7 @@ class NetESignal : public NetExpr {
|
|||
virtual bool set_width(unsigned, bool last_chance);
|
||||
|
||||
virtual NetESignal* dup_expr() const;
|
||||
NetNet* synthesize(Design*des, NetScope*scope);
|
||||
NetNet* synthesize(Design*des, NetScope*scope, NetExpr*root);
|
||||
NexusSet* nex_input(bool rem_out = true);
|
||||
|
||||
// This is the expression for selecting an array word, if this
|
||||
|
|
|
|||
8
pform.cc
8
pform.cc
|
|
@ -110,12 +110,12 @@ PTask* pform_push_task_scope(char*name, bool is_auto)
|
|||
PTask*task;
|
||||
if (pform_cur_generate) {
|
||||
task = new PTask(task_name, pform_cur_generate->lexical_scope,
|
||||
is_auto || debug_automatic);
|
||||
is_auto);
|
||||
pform_cur_generate->tasks[task->pscope_name()] = task;
|
||||
pform_cur_generate->lexical_scope = task;
|
||||
} else {
|
||||
task = new PTask(task_name, lexical_scope,
|
||||
is_auto || debug_automatic);
|
||||
is_auto);
|
||||
pform_cur_module->tasks[task->pscope_name()] = task;
|
||||
lexical_scope = task;
|
||||
}
|
||||
|
|
@ -130,12 +130,12 @@ PFunction* pform_push_function_scope(char*name, bool is_auto)
|
|||
PFunction*func;
|
||||
if (pform_cur_generate) {
|
||||
func = new PFunction(func_name, pform_cur_generate->lexical_scope,
|
||||
is_auto || debug_automatic);
|
||||
is_auto);
|
||||
pform_cur_generate->funcs[func->pscope_name()] = func;
|
||||
pform_cur_generate->lexical_scope = func;
|
||||
} else {
|
||||
func = new PFunction(func_name, lexical_scope,
|
||||
is_auto || debug_automatic);
|
||||
is_auto);
|
||||
pform_cur_module->funcs[func->pscope_name()] = func;
|
||||
lexical_scope = func;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -141,7 +141,7 @@ static void make_DFF_CE(Design*des, NetProcTop*top, NetEvWait*wclk,
|
|||
|
||||
NetEvProbe*pclk = eclk->probe(0);
|
||||
NetESignal*d = dynamic_cast<NetESignal*> (asn->rval());
|
||||
NetNet*ce = cexp? cexp->synthesize(des, top->scope()) : 0;
|
||||
NetNet*ce = cexp? cexp->synthesize(des, top->scope(), cexp) : 0;
|
||||
|
||||
if (d == 0) {
|
||||
cerr << asn->get_fileline() << ": internal error: "
|
||||
|
|
|
|||
6
synth.cc
6
synth.cc
|
|
@ -53,7 +53,7 @@ int do_expr::assign(NetAssign*stmt)
|
|||
if (dynamic_cast<NetESignal*>(stmt->rval()))
|
||||
return 0;
|
||||
|
||||
NetNet*tmp = stmt->rval()->synthesize(des_, scope_);
|
||||
NetNet*tmp = stmt->rval()->synthesize(des_, scope_, stmt->rval());
|
||||
if (tmp == 0)
|
||||
return 0;
|
||||
|
||||
|
|
@ -68,7 +68,7 @@ int do_expr::assign_nb(NetAssignNB*stmt)
|
|||
if (dynamic_cast<NetESignal*>(stmt->rval()))
|
||||
return 0;
|
||||
|
||||
NetNet*tmp = stmt->rval()->synthesize(des_, scope_);
|
||||
NetNet*tmp = stmt->rval()->synthesize(des_, scope_, stmt->rval());
|
||||
if (tmp == 0)
|
||||
return 0;
|
||||
|
||||
|
|
@ -82,7 +82,7 @@ int do_expr::condit(NetCondit*stmt)
|
|||
{
|
||||
/* synthesize the condition expression, if necessary. */
|
||||
if (! dynamic_cast<NetESignal*>(stmt->expr())) {
|
||||
NetNet*tmp = stmt->expr()->synthesize(des_, scope_);
|
||||
NetNet*tmp = stmt->expr()->synthesize(des_, scope_, stmt->expr());
|
||||
|
||||
if (tmp) {
|
||||
NetESignal*tmpe = new NetESignal(tmp);
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ bool NetProc::synth_sync(Design*des, NetScope*scope, NetFF*ff,
|
|||
bool NetAssignBase::synth_async(Design*des, NetScope*scope,
|
||||
const NetBus&nex_map, NetBus&nex_out)
|
||||
{
|
||||
NetNet*rsig = rval_->synthesize(des, scope);
|
||||
NetNet*rsig = rval_->synthesize(des, scope, rval_);
|
||||
assert(rsig);
|
||||
|
||||
NetNet*lsig = lval_->sig();
|
||||
|
|
@ -155,7 +155,7 @@ bool NetCase::synth_async(Design*des, NetScope*scope,
|
|||
const NetBus&nex_map, NetBus&nex_out)
|
||||
{
|
||||
/* Synthesize the select expression. */
|
||||
NetNet*esig = expr_->synthesize(des, scope);
|
||||
NetNet*esig = expr_->synthesize(des, scope, expr_);
|
||||
|
||||
unsigned sel_width = esig->vector_width();
|
||||
assert(sel_width > 0);
|
||||
|
|
@ -978,4 +978,3 @@ void synth2(Design*des)
|
|||
synth2_f synth_obj;
|
||||
des->functor(&synth_obj);
|
||||
}
|
||||
|
||||
|
|
|
|||
14
t-dll-api.cc
14
t-dll-api.cc
|
|
@ -1284,6 +1284,20 @@ extern "C" unsigned ivl_lpm_width(ivl_lpm_t net)
|
|||
return net->width;
|
||||
}
|
||||
|
||||
extern "C" ivl_event_t ivl_lpm_trigger(ivl_lpm_t net)
|
||||
{
|
||||
assert(net);
|
||||
switch (net->type) {
|
||||
case IVL_LPM_SFUNC:
|
||||
return net->u_.sfunc.trigger;
|
||||
case IVL_LPM_UFUNC:
|
||||
return net->u_.ufunc.trigger;
|
||||
default:
|
||||
assert(0);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" ivl_expr_t ivl_lval_mux(ivl_lval_t net)
|
||||
{
|
||||
assert(net);
|
||||
|
|
|
|||
40
t-dll.cc
40
t-dll.cc
|
|
@ -1239,6 +1239,40 @@ void dll_target::net_case_cmp(const NetCaseCmp*net)
|
|||
scope_add_lpm(obj->scope, obj);
|
||||
}
|
||||
|
||||
ivl_event_t dll_target::make_lpm_trigger(const NetEvWait*net)
|
||||
{
|
||||
ivl_event_t trigger = 0;
|
||||
if (net) {
|
||||
const NetEvent*ev = net->event(0);
|
||||
|
||||
/* Locate the event by name. */
|
||||
ivl_scope_t ev_scope = lookup_scope_(ev->scope());
|
||||
|
||||
assert(ev_scope);
|
||||
assert(ev_scope->nevent_ > 0);
|
||||
for (unsigned idx = 0; idx < ev_scope->nevent_; idx += 1) {
|
||||
const char*ename =
|
||||
ivl_event_basename(ev_scope->event_[idx]);
|
||||
if (strcmp(ev->name(), ename) == 0) {
|
||||
trigger = ev_scope->event_[idx];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Connect up the probe pins. This wasn't done during the
|
||||
::event method because the signals weren't scanned yet. */
|
||||
assert(ev->nprobe() == 1);
|
||||
const NetEvProbe*pr = ev->probe(0);
|
||||
for (unsigned bit = 0; bit < pr->pin_count(); bit += 1) {
|
||||
ivl_nexus_t nex = (ivl_nexus_t)
|
||||
pr->pin(bit).nexus()->t_cookie();
|
||||
assert(nex);
|
||||
trigger->pins[bit] = nex;
|
||||
}
|
||||
}
|
||||
return trigger;
|
||||
}
|
||||
|
||||
bool dll_target::net_sysfunction(const NetSysFunc*net)
|
||||
{
|
||||
unsigned idx;
|
||||
|
|
@ -1276,6 +1310,9 @@ bool dll_target::net_sysfunction(const NetSysFunc*net)
|
|||
IVL_DR_HiZ, IVL_DR_HiZ);
|
||||
}
|
||||
|
||||
/* Save information about the trigger event if it exists. */
|
||||
obj->u_.sfunc.trigger = make_lpm_trigger(net->trigger());
|
||||
|
||||
make_lpm_delays_(obj, net);
|
||||
|
||||
scope_add_lpm(obj->scope, obj);
|
||||
|
|
@ -1326,6 +1363,9 @@ bool dll_target::net_function(const NetUserFunc*net)
|
|||
nexus_lpm_add(obj->u_.ufunc.pins[idx], obj, idx, drive, drive);
|
||||
}
|
||||
|
||||
/* Save information about the trigger event if it exists. */
|
||||
obj->u_.ufunc.trigger = make_lpm_trigger(net->trigger());
|
||||
|
||||
make_lpm_delays_(obj, net);
|
||||
|
||||
/* All done. Add this LPM to the scope. */
|
||||
|
|
|
|||
4
t-dll.h
4
t-dll.h
|
|
@ -169,6 +169,8 @@ struct dll_target : public target_t, public expr_scan_t {
|
|||
void make_scope_parameters(ivl_scope_t scope, const NetScope*net);
|
||||
void make_scope_param_expr(ivl_parameter_t cur_par, NetExpr*etmp);
|
||||
|
||||
ivl_event_t make_lpm_trigger(const NetEvWait*ev);
|
||||
|
||||
static ivl_expr_t expr_from_value_(const verinum&that);
|
||||
};
|
||||
|
||||
|
|
@ -377,12 +379,14 @@ struct ivl_lpm_s {
|
|||
const char* fun_name;
|
||||
unsigned ports;
|
||||
ivl_nexus_t*pins;
|
||||
ivl_event_t trigger;
|
||||
} sfunc;
|
||||
|
||||
struct ivl_lpm_ufunc_s {
|
||||
ivl_scope_t def;
|
||||
unsigned ports;
|
||||
ivl_nexus_t*pins;
|
||||
ivl_event_t trigger;
|
||||
} ufunc;
|
||||
} u_;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1537,9 +1537,15 @@ static void draw_lpm_sfunc(ivl_lpm_t net)
|
|||
|
||||
const char*dly = draw_lpm_output_delay(net);
|
||||
|
||||
if (ivl_lpm_trigger(net))
|
||||
fprintf(vvp_out, "L_%p%s .sfunc/e %u %u \"%s\", E_%p", net, dly,
|
||||
ivl_file_table_index(ivl_lpm_file(net)),
|
||||
ivl_lpm_lineno(net), ivl_lpm_string(net),
|
||||
ivl_lpm_trigger(net));
|
||||
else
|
||||
fprintf(vvp_out, "L_%p%s .sfunc %u %u \"%s\"", net, dly,
|
||||
ivl_file_table_index(ivl_lpm_file(net)), ivl_lpm_lineno(net),
|
||||
ivl_lpm_string(net));
|
||||
ivl_file_table_index(ivl_lpm_file(net)),
|
||||
ivl_lpm_lineno(net), ivl_lpm_string(net));
|
||||
|
||||
/* Print the function type descriptor string. */
|
||||
fprintf(vvp_out, ", \"");
|
||||
|
|
@ -1565,6 +1571,11 @@ static void draw_lpm_ufunc(ivl_lpm_t net)
|
|||
|
||||
const char*dly = draw_lpm_output_delay(net);
|
||||
|
||||
if (ivl_lpm_trigger(net))
|
||||
fprintf(vvp_out, "L_%p%s .ufunc/e TD_%s, %u, E_%p", net, dly,
|
||||
vvp_mangle_id(ivl_scope_name(def)),
|
||||
ivl_lpm_width(net), ivl_lpm_trigger(net));
|
||||
else
|
||||
fprintf(vvp_out, "L_%p%s .ufunc TD_%s, %u", net, dly,
|
||||
vvp_mangle_id(ivl_scope_name(def)),
|
||||
ivl_lpm_width(net));
|
||||
|
|
|
|||
|
|
@ -654,9 +654,17 @@ input to port 1 is the amount to shift.
|
|||
|
||||
STRUCTURAL FUNCTION CALLS:
|
||||
|
||||
The .ufunc statement defines a call to a user defined function.
|
||||
The .ufunc statements define a call to a user defined function.
|
||||
|
||||
<label> .ufunc <flabel>, <wid>, <isymbols> ( <psymbols> ) <rsymbol> <ssymbol>;
|
||||
<label> .ufunc <flabel>, <wid>,
|
||||
<isymbols> ( <psymbols> ) <rsymbol> <ssymbol>;
|
||||
|
||||
<label> .ufunc/e <flabel>, <wid>, <trigger>,
|
||||
<isymbols> ( <psymbols> ) <rsymbol> <ssymbol>;
|
||||
|
||||
The first variant is used for functions that only need to be called
|
||||
when one of their inputs changes value. The second variant is used
|
||||
for functions that also need to be called when a trigger event occurs.
|
||||
|
||||
The <flabel> is the code label for the first instruction of the
|
||||
function implementation. This is code that the simulator will branch
|
||||
|
|
@ -664,6 +672,8 @@ to.
|
|||
|
||||
The <wid> is the width of the output vector in bits.
|
||||
|
||||
The <trigger> is the label for the trigger event.
|
||||
|
||||
The <isymbols> is a list of net symbols for each of the inputs to the
|
||||
function. These are points in the net, and the ufunc device watches
|
||||
these nets for input changes.
|
||||
|
|
|
|||
|
|
@ -225,7 +225,9 @@ extern void compile_extend_signed(char*label, long width, struct symb_s arg);
|
|||
|
||||
extern void compile_sfunc(char*label, char*name, char*format_string,
|
||||
long file_idx, long lineno,
|
||||
unsigned argc, struct symb_s*argv);
|
||||
unsigned argc, struct symb_s*argv,
|
||||
char*trigger_label);
|
||||
|
||||
extern void compile_repeat(char*label, long width, long repeat,
|
||||
struct symb_s arg);
|
||||
|
||||
|
|
@ -347,7 +349,8 @@ extern void compile_array_cleanup(void);
|
|||
extern void compile_ufunc(char*label, char*code, unsigned wid,
|
||||
unsigned argc, struct symb_s*argv,
|
||||
unsigned portc, struct symb_s*portv,
|
||||
struct symb_s retv, struct symb_s scope);
|
||||
struct symb_s retv, char*scope_label,
|
||||
char*trigger_label);
|
||||
|
||||
/*
|
||||
* The compile_event function takes the parts of the event statement
|
||||
|
|
|
|||
|
|
@ -157,6 +157,7 @@
|
|||
".resolv" { return K_RESOLV; }
|
||||
".scope" { return K_SCOPE; }
|
||||
".sfunc" { return K_SFUNC; }
|
||||
".sfunc/e" { return K_SFUNC_E; }
|
||||
".shift/l" { return K_SHIFTL; }
|
||||
".shift/r" { return K_SHIFTR; }
|
||||
".shift/rs" { return K_SHIFTRS; }
|
||||
|
|
@ -167,6 +168,7 @@
|
|||
".tranif1" { return K_TRANIF1; }
|
||||
".tranvp" { return K_TRANVP; }
|
||||
".ufunc" { return K_UFUNC; }
|
||||
".ufunc/e" { return K_UFUNC_E; }
|
||||
".var" { return K_VAR; }
|
||||
".var/real" { return K_VAR_R; }
|
||||
".var/s" { return K_VAR_S; }
|
||||
|
|
|
|||
38
vvp/parse.y
38
vvp/parse.y
|
|
@ -81,9 +81,9 @@ static struct __vpiModPath*modpath_dst = 0;
|
|||
%token K_PARAM_STR K_PARAM_L K_PARAM_REAL K_PART K_PART_PV
|
||||
%token K_PART_V K_PORT K_PV K_REDUCE_AND K_REDUCE_OR K_REDUCE_XOR
|
||||
%token K_REDUCE_NAND K_REDUCE_NOR K_REDUCE_XNOR K_REPEAT
|
||||
%token K_RESOLV K_SCOPE K_SFUNC K_SHIFTL K_SHIFTR K_SHIFTRS
|
||||
%token K_THREAD K_TIMESCALE K_TRAN K_TRANIF0 K_TRANIF1 K_TRANVP K_UFUNC
|
||||
%token K_UDP K_UDP_C K_UDP_S
|
||||
%token K_RESOLV K_SCOPE K_SFUNC K_SFUNC_E K_SHIFTL K_SHIFTR K_SHIFTRS
|
||||
%token K_THREAD K_TIMESCALE K_TRAN K_TRANIF0 K_TRANIF1 K_TRANVP
|
||||
%token K_UFUNC K_UFUNC_E K_UDP K_UDP_C K_UDP_S
|
||||
%token K_VAR K_VAR_S K_VAR_I K_VAR_R K_vpi_call K_vpi_func K_vpi_func_r
|
||||
%token K_disable K_fork
|
||||
%token K_ivl_version K_vpi_module K_vpi_time_precision K_file_names
|
||||
|
|
@ -218,12 +218,19 @@ statement
|
|||
other thread code that is automatically invoked if any of the
|
||||
bits in the symbols list change. */
|
||||
|
||||
| T_LABEL K_UFUNC T_SYMBOL ',' T_NUMBER ',' symbols
|
||||
'(' symbols ')' symbol symbol ';'
|
||||
| T_LABEL K_UFUNC T_SYMBOL ',' T_NUMBER ','
|
||||
symbols '(' symbols ')' symbol T_SYMBOL ';'
|
||||
{ compile_ufunc($1, $3, $5,
|
||||
$7.cnt, $7.vect,
|
||||
$9.cnt, $9.vect,
|
||||
$11, $12); }
|
||||
$11, $12, 0); }
|
||||
|
||||
| T_LABEL K_UFUNC_E T_SYMBOL ',' T_NUMBER ',' T_SYMBOL ','
|
||||
symbols '(' symbols ')' symbol T_SYMBOL ';'
|
||||
{ compile_ufunc($1, $3, $5,
|
||||
$9.cnt, $9.vect,
|
||||
$11.cnt, $11.vect,
|
||||
$13, $14, $7); }
|
||||
|
||||
/* Resolver statements are very much like functors. They are
|
||||
compiled to functors of a different mode. */
|
||||
|
|
@ -460,13 +467,22 @@ statement
|
|||
{ compile_extend_signed($1, $3, $5); }
|
||||
|
||||
/* System function call */
|
||||
| T_LABEL K_SFUNC T_NUMBER T_NUMBER T_STRING ',' T_STRING ','
|
||||
symbols ';'
|
||||
{ compile_sfunc($1, $5, $7, $3, $4, $9.cnt, $9.vect); }
|
||||
| T_LABEL K_SFUNC T_NUMBER T_NUMBER T_STRING ','
|
||||
T_STRING ',' symbols ';'
|
||||
{ compile_sfunc($1, $5, $7, $3, $4, $9.cnt, $9.vect, 0); }
|
||||
|
||||
| T_LABEL K_SFUNC_E T_NUMBER T_NUMBER T_STRING ',' T_SYMBOL ','
|
||||
T_STRING ',' symbols ';'
|
||||
{ compile_sfunc($1, $5, $9, $3, $4, $11.cnt, $11.vect, $7); }
|
||||
|
||||
/* System function call - no arguments */
|
||||
| T_LABEL K_SFUNC T_NUMBER T_NUMBER T_STRING ',' T_STRING ';'
|
||||
{ compile_sfunc($1, $5, $7, $3, $4, 0, NULL); }
|
||||
| T_LABEL K_SFUNC T_NUMBER T_NUMBER T_STRING ','
|
||||
T_STRING ';'
|
||||
{ compile_sfunc($1, $5, $7, $3, $4, 0, 0, 0); }
|
||||
|
||||
| T_LABEL K_SFUNC_E T_NUMBER T_NUMBER T_STRING ',' T_SYMBOL ','
|
||||
T_STRING ';'
|
||||
{ compile_sfunc($1, $5, $9, $3, $4, 0, 0, $7); }
|
||||
|
||||
/* Shift nodes. */
|
||||
|
||||
|
|
|
|||
18
vvp/sfunc.cc
18
vvp/sfunc.cc
|
|
@ -41,6 +41,16 @@ sfunc_core::~sfunc_core()
|
|||
{
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is only called when a trigger event occurs. Just arrange for
|
||||
* the function to be called.
|
||||
*/
|
||||
void sfunc_core::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
|
||||
vvp_context_t)
|
||||
{
|
||||
schedule_generic(this, 0, false);
|
||||
}
|
||||
|
||||
void sfunc_core::recv_vec4_from_inputs(unsigned port)
|
||||
{
|
||||
vpiHandle vpi = argv_[port];
|
||||
|
|
@ -130,7 +140,8 @@ static int make_vpi_argv(unsigned argc, vpiHandle*vpi_argv,
|
|||
|
||||
void compile_sfunc(char*label, char*name, char*format_string,
|
||||
long file_idx, long lineno,
|
||||
unsigned argc, struct symb_s*argv)
|
||||
unsigned argc, struct symb_s*argv,
|
||||
char*trigger_label)
|
||||
{
|
||||
vpiHandle*vpi_argv = new vpiHandle[argc];
|
||||
int width_code = make_vpi_argv(argc, vpi_argv, format_string);
|
||||
|
|
@ -151,4 +162,9 @@ void compile_sfunc(char*label, char*name, char*format_string,
|
|||
/* Link the inputs to the functor. */
|
||||
wide_inputs_connect(score, argc, argv);
|
||||
free(argv);
|
||||
|
||||
/* If this function has a trigger event, connect the functor to
|
||||
that event. */
|
||||
if (trigger_label)
|
||||
input_connect(ptr, 0, trigger_label);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@ class sfunc_core : public vvp_wide_fun_core, protected vvp_gen_event_s {
|
|||
sfunc_core(vvp_net_t*ptr, vpiHandle sys, unsigned argc, vpiHandle*argv);
|
||||
~sfunc_core();
|
||||
|
||||
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
|
||||
vvp_context_t context);
|
||||
|
||||
private:
|
||||
void recv_vec4_from_inputs(unsigned port);
|
||||
void recv_real_from_inputs(unsigned port);
|
||||
|
|
|
|||
20
vvp/ufunc.cc
20
vvp/ufunc.cc
|
|
@ -90,6 +90,16 @@ void ufunc_core::finish_thread(vthread_t thr)
|
|||
propagate_vec4(sig->vec4_value());
|
||||
}
|
||||
|
||||
/*
|
||||
* This method is only called when a trigger event occurs. Just arrange for
|
||||
* the function to be called.
|
||||
*/
|
||||
void ufunc_core::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
|
||||
vvp_context_t)
|
||||
{
|
||||
invoke_thread_();
|
||||
}
|
||||
|
||||
/*
|
||||
* The recv_vec4 methods of the input functors call this to assign the
|
||||
* input value to the port of the functor. I save the input value and
|
||||
|
|
@ -126,7 +136,8 @@ void ufunc_core::invoke_thread_()
|
|||
void compile_ufunc(char*label, char*code, unsigned wid,
|
||||
unsigned argc, struct symb_s*argv,
|
||||
unsigned portc, struct symb_s*portv,
|
||||
struct symb_s retv, struct symb_s scope)
|
||||
struct symb_s retv, char*scope_label,
|
||||
char*trigger_label)
|
||||
{
|
||||
/* The input argument list and port list must have the same
|
||||
sizes, since internally we will be mapping the inputs list
|
||||
|
|
@ -170,7 +181,7 @@ void compile_ufunc(char*label, char*code, unsigned wid,
|
|||
vvp_net_t*ptr = new vvp_net_t;
|
||||
ufunc_core*fcore = new ufunc_core(wid, ptr, portc, ports,
|
||||
start_code, call_scope,
|
||||
retv.text, scope.text);
|
||||
retv.text, scope_label);
|
||||
ptr->fun = fcore;
|
||||
define_functor_symbol(label, ptr);
|
||||
free(label);
|
||||
|
|
@ -179,6 +190,11 @@ void compile_ufunc(char*label, char*code, unsigned wid,
|
|||
|
||||
wide_inputs_connect(fcore, argc, argv);
|
||||
|
||||
/* If this function has a trigger event, connect the functor to
|
||||
that event. */
|
||||
if (trigger_label)
|
||||
input_connect(ptr, 0, trigger_label);
|
||||
|
||||
free(argv);
|
||||
free(portv);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ class ufunc_core : public vvp_wide_fun_core {
|
|||
void assign_bits_to_ports(vvp_context_t context);
|
||||
void finish_thread(vthread_t thr);
|
||||
|
||||
void recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit,
|
||||
vvp_context_t context);
|
||||
|
||||
private:
|
||||
void recv_vec4_from_inputs(unsigned port);
|
||||
void recv_real_from_inputs(unsigned port);
|
||||
|
|
|
|||
|
|
@ -360,6 +360,19 @@ static vpiHandle sysfunc_put_4net_value(vpiHandle ref, p_vpi_value vp, int)
|
|||
break;
|
||||
}
|
||||
|
||||
case vpiTimeVal: {
|
||||
unsigned long tmp = vp->value.time->low;
|
||||
for (unsigned idx = 0 ; idx < vwid ; idx += 1) {
|
||||
val.set_bit(idx, (tmp&1)? BIT4_1 : BIT4_0);
|
||||
|
||||
if (idx == 31)
|
||||
tmp = vp->value.time->high;
|
||||
else
|
||||
tmp >>= 1;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case vpiVectorVal:
|
||||
|
||||
for (unsigned wdx = 0 ; wdx < vwid ; wdx += 32) {
|
||||
|
|
|
|||
Loading…
Reference in New Issue