diff --git a/PExpr.cc b/PExpr.cc index 96428204f..751d2be5f 100644 --- a/PExpr.cc +++ b/PExpr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2007 Stephen Williams + * Copyright (c) 1998-2008 Stephen Williams * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -20,6 +20,7 @@ # include "config.h" # include +# include # include "PExpr.h" # include "Module.h" @@ -117,6 +118,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&p, PExpr*r) : parms_(p), repeat_(r) { @@ -299,4 +320,3 @@ bool PEUnary::is_constant(Module*m) const { return expr_->is_constant(m); } - diff --git a/PExpr.h b/PExpr.h index a06a59015..fdb0d725f 100644 --- a/PExpr.h +++ b/PExpr.h @@ -710,6 +710,8 @@ class PECallFunction : public PExpr { explicit PECallFunction(perm_string n); ~PECallFunction(); + virtual bool is_constant(Module*) const; + virtual void dump(ostream &) const; virtual NetNet* elaborate_net(Design*des, NetScope*scope, @@ -721,6 +723,7 @@ class PECallFunction : public PExpr { Link::strength_t drive1) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, 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, unsigned min, unsigned lval, diff --git a/PFunction.cc b/PFunction.cc index fb26c434b..1a3146a8a 100644 --- a/PFunction.cc +++ b/PFunction.cc @@ -21,9 +21,10 @@ #include "PTask.h" -PFunction::PFunction(perm_string name, PScope*parent) +PFunction::PFunction(perm_string name, PScope*parent, bool is_auto) : PScope(name, parent), ports_(0), statement_(0) { + is_auto_ = is_auto; return_type_.type = PTF_NONE; } diff --git a/PTask.cc b/PTask.cc index b829e03f2..3719c4b3c 100644 --- a/PTask.cc +++ b/PTask.cc @@ -21,9 +21,10 @@ # include "PTask.h" -PTask::PTask(perm_string name, PScope*parent) +PTask::PTask(perm_string name, PScope*parent, bool is_auto) : PScope(name, parent), ports_(0), statement_(0) { + is_auto_ = is_auto; } PTask::~PTask() @@ -41,31 +42,3 @@ void PTask::set_statement(Statement*s) assert(statement_ == 0); statement_ = s; } - - -/* - * $Log: PTask.cc,v $ - * Revision 1.7 2002/08/12 01:34:58 steve - * conditional ident string using autoconfig. - * - * Revision 1.6 2001/07/25 03:10:48 steve - * Create a config.h.in file to hold all the config - * junk, and support gcc 3.0. (Stephan Boettcher) - * - * Revision 1.5 2001/04/19 03:04:47 steve - * Spurious assert of empty statemnt. - * - * Revision 1.4 2001/01/13 22:20:08 steve - * Parse parameters within nested scopes. - * - * Revision 1.3 2000/02/23 02:56:53 steve - * Macintosh compilers do not support ident. - * - * Revision 1.2 1999/07/24 02:11:19 steve - * Elaborate task input ports. - * - * Revision 1.1 1999/07/03 02:12:51 steve - * Elaborate user defined tasks. - * - */ - diff --git a/PTask.h b/PTask.h index 4d9c24e9c..de05ac053 100644 --- a/PTask.h +++ b/PTask.h @@ -51,7 +51,7 @@ struct PTaskFuncArg { class PTask : public PScope, public LineInfo { public: - explicit PTask(perm_string name, PScope*parent); + explicit PTask(perm_string name, PScope*parent, bool is_auto); ~PTask(); void set_ports(svector*p); @@ -69,11 +69,14 @@ class PTask : public PScope, public LineInfo { // Elaborate the statement to finish off the task definition. void elaborate(Design*des, NetScope*scope) const; + bool is_auto() const { return is_auto_; }; + void dump(ostream&, unsigned) const; private: svector*ports_; Statement*statement_; + bool is_auto_; private: // Not implemented PTask(const PTask&); @@ -90,7 +93,7 @@ class PTask : public PScope, public LineInfo { class PFunction : public PScope, public LineInfo { public: - explicit PFunction(perm_string name, PScope*parent); + explicit PFunction(perm_string name, PScope*parent, bool is_auto); ~PFunction(); void set_ports(svector*p); @@ -105,12 +108,15 @@ class PFunction : public PScope, public LineInfo { /* Elaborate the behavioral statement. */ void elaborate(Design *des, NetScope*) const; + bool is_auto() const { return is_auto_; }; + void dump(ostream&, unsigned) const; private: PTaskFuncArg return_type_; svector *ports_; Statement *statement_; + bool is_auto_; }; #endif diff --git a/design_dump.cc b/design_dump.cc index ca8e71d8e..1af627745 100644 --- a/design_dump.cc +++ b/design_dump.cc @@ -998,6 +998,7 @@ void NetScope::dump(ostream&o) const o << " generate block"; break; } + if (is_auto()) o << " (automatic)"; o << endl; for (unsigned idx = 0 ; idx < attr_cnt() ; idx += 1) diff --git a/elab_expr.cc b/elab_expr.cc index 2258ef78c..c496b04de 100644 --- a/elab_expr.cc +++ b/elab_expr.cc @@ -653,7 +653,7 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope, int expr_w delete tmp; } - verinum val (sub_expr_width, 8*sizeof(unsigned)); + verinum val ( (uint64_t)sub_expr_width, 8*sizeof(unsigned)); NetEConst*sub = new NetEConst(val); sub->set_line(*this); @@ -1653,7 +1653,7 @@ NetExpr* PEIdent::elaborate_expr_net_word_(Design*des, NetScope*scope, // Recalculate the constant address with the adjusted base. unsigned use_addr = net->array_index_to_address(addr); if (addr < 0 || use_addr != (unsigned long)addr) { - verinum val (use_addr, 8*sizeof(use_addr)); + verinum val ( (uint64_t)use_addr, 8*sizeof(use_addr)); NetEConst*tmp = new NetEConst(val); tmp->set_line(*this); delete word_index; diff --git a/elab_pexpr.cc b/elab_pexpr.cc index a3d398a47..e4cdbe087 100644 --- a/elab_pexpr.cc +++ b/elab_pexpr.cc @@ -25,6 +25,7 @@ # include "netmisc.h" # include +# include # include # include "ivl_assert.h" @@ -267,3 +268,40 @@ NetExpr*PEUnary::elaborate_pexpr (Design*des, NetScope*scope) const 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; +} diff --git a/elab_scope.cc b/elab_scope.cc index b77292dea..5df05b7bc 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -150,6 +150,7 @@ static void elaborate_scope_tasks(Design*des, NetScope*scope, } NetScope*task_scope = new NetScope(scope, use_name, NetScope::TASK); + task_scope->is_auto((*cur).second->is_auto()); task_scope->set_line((*cur).second); if (debug_scopes) @@ -179,6 +180,7 @@ static void elaborate_scope_funcs(Design*des, NetScope*scope, } NetScope*func_scope = new NetScope(scope, use_name, NetScope::FUNC); + func_scope->is_auto((*cur).second->is_auto()); func_scope->set_line((*cur).second); if (debug_scopes) diff --git a/elaborate.cc b/elaborate.cc index 23ce93fff..deaa14d1a 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -2532,8 +2532,8 @@ NetProc* PEventStatement::elaborate_st(Design*des, NetScope*scope, } NexusSet*nset = enet->nex_input(rem_out); if (nset == 0) { - cerr << get_fileline() << ": internal error: No NexusSet" - << " from statement." << endl; + cerr << get_fileline() << ": error: Unable to elaborate:" + << endl; enet->dump(cerr, 6); des->errors += 1; return enet; diff --git a/eval_tree.cc b/eval_tree.cc index 4a831ce2f..422e14f48 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -22,6 +22,8 @@ # include # include +# include +# include # include "netlist.h" # include "ivl_assert.h" @@ -1620,3 +1622,67 @@ NetEConst* NetEUReduce::eval_tree(int prune_to_width) return new NetEConst(verinum(res, 1)); } + +NetExpr* evaluate_clog2(NetExpr*arg) +{ + NetEConst*tmpi = dynamic_cast(arg); + NetECReal*tmpr = dynamic_cast(arg); + bool is_neg = false; + if (tmpi || tmpr) { + verinum arg; + if (tmpi) { + arg = tmpi->value(); + } else { + arg = verinum(tmpr->value().as_double(), true); + } + + /* If we have an x in the verinum we return 32'bx. */ + if (!arg.is_defined()) { + verinum tmp (verinum::Vx, 32); + tmp.has_sign(true); + NetEConst*rtn = new NetEConst(tmp); + return rtn; + } + + 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; + verinum tmp (res, 32); + tmp.has_sign(true); + NetEConst*rtn = new NetEConst(tmp); + 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; +} diff --git a/ivl.def b/ivl.def index dfcf064cf..22260e7e9 100644 --- a/ivl.def +++ b/ivl.def @@ -143,6 +143,7 @@ ivl_scope_def_lineno ivl_scope_event ivl_scope_events ivl_scope_file +ivl_scope_is_auto ivl_scope_lineno ivl_scope_logs ivl_scope_log diff --git a/ivl_target.h b/ivl_target.h index 7535bd0d4..f04d90f8a 100644 --- a/ivl_target.h +++ b/ivl_target.h @@ -1447,6 +1447,9 @@ extern unsigned ivl_parameter_lineno(ivl_parameter_t net); * ivl_scope_lineno * Returns the instantiation file and line for this scope. * + * ivl_scope_is_auto + * Is the task or function declared to be automatic? + * * ivl_scope_var * ivl_scope_vars * REMOVED @@ -1523,6 +1526,7 @@ extern unsigned ivl_scope_def_lineno(ivl_scope_t net); extern unsigned ivl_scope_events(ivl_scope_t net); extern ivl_event_t ivl_scope_event(ivl_scope_t net, unsigned idx); extern const char* ivl_scope_file(ivl_scope_t net); +extern unsigned ivl_scope_is_auto(ivl_scope_t net); extern unsigned ivl_scope_lineno(ivl_scope_t net); extern unsigned ivl_scope_logs(ivl_scope_t net); extern ivl_net_logic_t ivl_scope_log(ivl_scope_t net, unsigned idx); diff --git a/lexor_keyword.gperf b/lexor_keyword.gperf index 980b33c89..9cb72b281 100644 --- a/lexor_keyword.gperf +++ b/lexor_keyword.gperf @@ -25,6 +25,7 @@ assign, GN_KEYWORDS_1364_1995, K_assign atan, GN_KEYWORDS_VAMS_2_3, K_atan atan2, GN_KEYWORDS_VAMS_2_3, K_atan2 atanh, GN_KEYWORDS_VAMS_2_3, K_atanh +automatic, GN_KEYWORDS_1364_2001, K_automatic begin, GN_KEYWORDS_1364_1995, K_begin bool, GN_KEYWORDS_ICARUS, K_bool buf, GN_KEYWORDS_1364_1995, K_buf diff --git a/net_nex_input.cc b/net_nex_input.cc index 2a68c2a8b..0f97c2a44 100644 --- a/net_nex_input.cc +++ b/net_nex_input.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2007 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2008 Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it * and/or modify it in source code form under the terms of the GNU @@ -53,8 +53,13 @@ NexusSet* NetEBinary::nex_input(bool rem_out) NexusSet* NetEConcat::nex_input(bool rem_out) { + if (parms_[0] == NULL) return NULL; NexusSet*result = parms_[0]->nex_input(rem_out); for (unsigned idx = 1 ; idx < parms_.count() ; idx += 1) { + if (parms_[idx] == NULL) { + delete result; + return NULL; + } NexusSet*tmp = parms_[idx]->nex_input(rem_out); result->add(*tmp); delete tmp; @@ -98,6 +103,10 @@ NexusSet* NetESelect::nex_input(bool rem_out) { NexusSet*result = base_? base_->nex_input(rem_out) : new NexusSet(); NexusSet*tmp = expr_->nex_input(rem_out); + if (tmp == NULL) { + delete result; + return NULL; + } result->add(*tmp); delete tmp; return result; @@ -381,4 +390,3 @@ NexusSet* NetWhile::nex_input(bool rem_out) delete tmp; return result; } - diff --git a/net_scope.cc b/net_scope.cc index 2b8baf0cb..d39152cdc 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -41,6 +41,7 @@ NetScope::NetScope(NetScope*up, const hname_t&n, NetScope::TYPE t) signals_ = 0; events_ = 0; lcounter_ = 0; + is_auto_ = false; if (up) { default_nettype_ = up->default_nettype(); @@ -261,6 +262,7 @@ NetFuncDef* NetScope::func_def() assert( type_ == FUNC ); return func_; } + bool NetScope::in_func() { return (type_ == FUNC) ? true : false; diff --git a/netlist.h b/netlist.h index aaef219d2..633092c58 100644 --- a/netlist.h +++ b/netlist.h @@ -697,6 +697,9 @@ class NetScope : public Attrib { unsigned get_def_lineno() const { return def_lineno_; }; bool in_func(); + /* Is the task or function automatic. */ + void is_auto(bool is_auto) { is_auto_ = is_auto; }; + bool is_auto() const { return is_auto_; }; const NetTaskDef* task_def() const; const NetFuncDef* func_def() const; @@ -840,6 +843,7 @@ class NetScope : public Attrib { NetScope*sub_; unsigned lcounter_; + bool is_auto_; }; /* @@ -3455,6 +3459,8 @@ class NetESFunc : public NetExpr { NetExpr* parm(unsigned idx); const NetExpr* parm(unsigned idx) const; + virtual NetExpr* eval_tree(int prune_to_width = -1); + virtual ivl_variable_type_t expr_type() const; virtual NexusSet* nex_input(bool rem_out = true); virtual bool set_width(unsigned, bool last_chance); diff --git a/parse.y b/parse.y index 7382eae6b..4b2dae490 100644 --- a/parse.y +++ b/parse.y @@ -205,7 +205,7 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2) %token K_PSTAR K_STARP %token K_LOR K_LAND K_NAND K_NOR K_NXOR K_TRIGGER %token K_abs K_abstol K_access K_acos K_acosh K_asin K_analog K_asinh -%token K_atan K_atanh K_atan2 +%token K_atan K_atanh K_atan2 K_automatic %token K_always K_and K_assign K_begin K_bool K_buf K_bufif0 K_bufif1 K_case %token K_casex K_casez K_ceil K_cmos K_continuous K_cos K_cosh %token K_ddt_nature K_deassign K_default K_defparam K_disable K_discrete @@ -239,7 +239,7 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2) %type from_exclude %type number -%type signed_opt udp_reg_opt edge_operator +%type signed_opt udp_reg_opt edge_operator automatic_opt %type drive_strength drive_strength_opt dr_strength0 dr_strength1 %type udp_input_sym udp_output_sym %type udp_input_list udp_sequ_entry udp_comb_entry @@ -2094,41 +2094,41 @@ module_item statements in the task body. But we continue to accept it as an extension. */ - | K_task IDENTIFIER ';' + | K_task automatic_opt IDENTIFIER ';' { assert(current_task == 0); - current_task = pform_push_task_scope($2); + current_task = pform_push_task_scope($3, $2); FILE_NAME(current_task, @1); } task_item_list_opt statement_or_null K_endtask - { current_task->set_ports($5); - current_task->set_statement($6); + { current_task->set_ports($6); + current_task->set_statement($7); pform_pop_scope(); current_task = 0; - delete[]$2; + delete[]$3; } - | K_task IDENTIFIER + | K_task automatic_opt IDENTIFIER { assert(current_task == 0); - current_task = pform_push_task_scope($2); + current_task = pform_push_task_scope($3, $2); FILE_NAME(current_task, @1); } '(' task_port_decl_list ')' ';' block_item_decls_opt statement_or_null K_endtask - { current_task->set_ports($5); - current_task->set_statement($9); + { current_task->set_ports($6); + current_task->set_statement($10); pform_pop_scope(); current_task = 0; - delete[]$2; + delete[]$3; } - | K_task IDENTIFIER error K_endtask + | K_task automatic_opt IDENTIFIER error K_endtask { pform_pop_scope(); current_task = 0; - delete[]$2; + delete[]$3; } /* The function declaration rule matches the function declaration @@ -2136,42 +2136,42 @@ module_item definitions in the func_body to take on the scope of the function instead of the module. */ - | K_function function_range_or_type_opt IDENTIFIER ';' + | K_function automatic_opt function_range_or_type_opt IDENTIFIER ';' { assert(current_function == 0); - current_function = pform_push_function_scope($3); + current_function = pform_push_function_scope($4, $2); FILE_NAME(current_function, @1); } function_item_list statement K_endfunction - { current_function->set_ports($6); - current_function->set_statement($7); - current_function->set_return($2); + { current_function->set_ports($7); + current_function->set_statement($8); + current_function->set_return($3); pform_pop_scope(); current_function = 0; - delete[]$3; + delete[]$4; } - | K_function function_range_or_type_opt IDENTIFIER + | K_function automatic_opt function_range_or_type_opt IDENTIFIER { assert(current_function == 0); - current_function = pform_push_function_scope($3); + current_function = pform_push_function_scope($4, $2); FILE_NAME(current_function, @1); } '(' task_port_decl_list ')' ';' block_item_decls_opt statement K_endfunction - { current_function->set_ports($6); - current_function->set_statement($10); - current_function->set_return($2); + { current_function->set_ports($7); + current_function->set_statement($11); + current_function->set_return($3); pform_pop_scope(); current_function = 0; - delete[]$3; + delete[]$4; } - | K_function function_range_or_type_opt IDENTIFIER error K_endfunction + | K_function automatic_opt function_range_or_type_opt IDENTIFIER error K_endfunction { pform_pop_scope(); current_task = 0; - delete[]$3; + delete[]$4; } /* A generate region can contain further module items. Actually, it @@ -2278,6 +2278,11 @@ module_item { yyerror(@1, "error: Malformed $attribute parameter list."); } ; +automatic_opt + : K_automatic { $$ = true; } + | { $$ = false;} + ; + generate_if : K_if '(' expression ')' { pform_start_generate_if(@1, $3); } generate_case_items diff --git a/pform.cc b/pform.cc index 6b3612a06..7057376cc 100644 --- a/pform.cc +++ b/pform.cc @@ -103,17 +103,18 @@ void pform_pop_scope() } } -PTask* pform_push_task_scope(char*name) +PTask* pform_push_task_scope(char*name, bool is_auto) { perm_string task_name = lex_strings.make(name); PTask*task; if (pform_cur_generate) { - task = new PTask(task_name, pform_cur_generate->lexical_scope); + task = new PTask(task_name, pform_cur_generate->lexical_scope, + is_auto); pform_cur_generate->tasks[task->pscope_name()] = task; pform_cur_generate->lexical_scope = task; } else { - task = new PTask(task_name, lexical_scope); + task = new PTask(task_name, lexical_scope, is_auto); pform_cur_module->tasks[task->pscope_name()] = task; lexical_scope = task; } @@ -121,17 +122,18 @@ PTask* pform_push_task_scope(char*name) return task; } -PFunction* pform_push_function_scope(char*name) +PFunction* pform_push_function_scope(char*name, bool is_auto) { perm_string func_name = lex_strings.make(name); PFunction*func; if (pform_cur_generate) { - func = new PFunction(func_name, pform_cur_generate->lexical_scope); + func = new PFunction(func_name, pform_cur_generate->lexical_scope, + is_auto); pform_cur_generate->funcs[func->pscope_name()] = func; pform_cur_generate->lexical_scope = func; } else { - func = new PFunction(func_name, lexical_scope); + func = new PFunction(func_name, lexical_scope, is_auto); pform_cur_module->funcs[func->pscope_name()] = func; lexical_scope = func; } diff --git a/pform.h b/pform.h index a94be9f96..ae410c8e7 100644 --- a/pform.h +++ b/pform.h @@ -175,8 +175,8 @@ extern void pform_make_udp(perm_string name, */ extern void pform_pop_scope(); -extern PTask*pform_push_task_scope(char*name); -extern PFunction*pform_push_function_scope(char*name); +extern PTask*pform_push_task_scope(char*name, bool is_auto); +extern PFunction*pform_push_function_scope(char*name, bool is_auto); extern PBlock*pform_push_block_scope(char*name, PBlock::BL_TYPE tt); diff --git a/pform_dump.cc b/pform_dump.cc index ba6e60912..f506bd655 100644 --- a/pform_dump.cc +++ b/pform_dump.cc @@ -715,6 +715,7 @@ void PForStatement::dump(ostream&out, unsigned ind) const void PFunction::dump(ostream&out, unsigned ind) const { out << setw(ind) << "" << "function "; + if (is_auto_) cout << "automatic "; switch (return_type_.type) { case PTF_NONE: out << "?none? "; @@ -775,6 +776,9 @@ void PRepeat::dump(ostream&out, unsigned ind) const void PTask::dump(ostream&out, unsigned ind) const { + out << setw(ind) << "" << "task "; + if (is_auto_) cout << "automatic "; + out << pscope_name() << ";" << endl; if (ports_) for (unsigned idx = 0 ; idx < ports_->count() ; idx += 1) { out << setw(ind) << ""; diff --git a/t-dll-api.cc b/t-dll-api.cc index e9db3bf5e..379053b45 100644 --- a/t-dll-api.cc +++ b/t-dll-api.cc @@ -1547,6 +1547,12 @@ extern "C" const char*ivl_scope_file(ivl_scope_t net) return net->file.str(); } +extern "C" unsigned ivl_scope_is_auto(ivl_scope_t net) +{ + assert(net); + return net->is_auto; +} + extern "C" unsigned ivl_scope_lineno(ivl_scope_t net) { assert(net); diff --git a/t-dll.cc b/t-dll.cc index e11692145..98ade6c15 100644 --- a/t-dll.cc +++ b/t-dll.cc @@ -575,6 +575,7 @@ void dll_target::add_root(ivl_design_s &des_, const NetScope *s) root_->time_units = s->time_unit(); root_->nattr = s->attr_cnt(); root_->attr = fill_in_attributes(s); + root_->is_auto = 0; des_.nroots_++; if (des_.roots_) @@ -2319,7 +2320,8 @@ void dll_target::scope(const NetScope*net) scope->time_precision = net->time_precision(); scope->time_units = net->time_unit(); scope->nattr = net->attr_cnt(); - scope->attr = fill_in_attributes(net); + scope->attr = fill_in_attributes(net); + scope->is_auto = net->is_auto(); switch (net->type()) { case NetScope::MODULE: diff --git a/t-dll.h b/t-dll.h index c409a6ce9..f5d6fd4b7 100644 --- a/t-dll.h +++ b/t-dll.h @@ -590,6 +590,7 @@ struct ivl_scope_s { /* Scopes that are tasks/functions have a definition. */ ivl_statement_t def; + unsigned is_auto; unsigned ports; ivl_signal_t*port; diff --git a/tgt-stub/stub.c b/tgt-stub/stub.c index 927312765..aba418c14 100644 --- a/tgt-stub/stub.c +++ b/tgt-stub/stub.c @@ -1448,12 +1448,13 @@ static int show_scope(ivl_scope_t net, void*x) ivl_scope_name(net), ivl_scope_params(net), ivl_scope_sigs(net), ivl_scope_logs(net)); + char *is_auto = ivl_scope_is_auto(net) ? "automatic " : ""; switch (ivl_scope_type(net)) { case IVL_SCT_MODULE: fprintf(out, " module %s", ivl_scope_tname(net)); break; case IVL_SCT_FUNCTION: - fprintf(out, " function %s", ivl_scope_tname(net)); + fprintf(out, " function %s%s", is_auto, ivl_scope_tname(net)); break; case IVL_SCT_BEGIN: fprintf(out, " begin : %s", ivl_scope_tname(net)); @@ -1462,7 +1463,7 @@ static int show_scope(ivl_scope_t net, void*x) fprintf(out, " fork : %s", ivl_scope_tname(net)); break; case IVL_SCT_TASK: - fprintf(out, " task %s", ivl_scope_tname(net)); + fprintf(out, " task %s%s", is_auto, ivl_scope_tname(net)); break; default: fprintf(out, " type(%u) %s", ivl_scope_type(net), diff --git a/tgt-vvp/vvp_scope.c b/tgt-vvp/vvp_scope.c index 42f378711..433263b24 100644 --- a/tgt-vvp/vvp_scope.c +++ b/tgt-vvp/vvp_scope.c @@ -1769,6 +1769,13 @@ int draw_scope(ivl_scope_t net, ivl_scope_t parent) { unsigned idx; const char *type; + /* For now we do not support automatic tasks or functions. */ + if (ivl_scope_is_auto(net)) { + fprintf(stderr, "%s:%u: vvp-tgt sorry: automatic tasks/functions " + "are not supported!\n", + ivl_scope_def_file(net), ivl_scope_def_lineno(net)); + exit(1); + } switch (ivl_scope_type(net)) { case IVL_SCT_MODULE: type = "module"; break; case IVL_SCT_FUNCTION: type = "function"; break; diff --git a/verinum.cc b/verinum.cc index 87f28de04..b4f20f32e 100644 --- a/verinum.cc +++ b/verinum.cc @@ -152,6 +152,112 @@ 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 or +/- infinity. */ + if (val != val || (val && (val == 0.5*val))) { + nbits_ = 1; + bits_ = new V[nbits_]; + bits_[0] = Vx; + 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) { string_flag_ = that.string_flag_; @@ -336,14 +442,9 @@ double verinum::as_double() const { 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; - if (signed_flag) { + /* Do we have/want a signed value? */ + if (has_sign_ && bits_[nbits_-1] == V1) { V carry = V1; for (unsigned idx = 0; idx < nbits_; idx += 1) { V sum = add_with_carry(~bits_[idx], V0, carry); @@ -351,7 +452,6 @@ double verinum::as_double() const val += pow(2.0, (double)idx); } val *= -1.0; -// val = (double) as_long(); } else { for (unsigned idx = 0; idx < nbits_; idx += 1) { if (bits_[idx] == V1) diff --git a/verinum.h b/verinum.h index b184c3c8c..5142883ee 100644 --- a/verinum.h +++ b/verinum.h @@ -46,6 +46,7 @@ class verinum { verinum(const V*v, unsigned nbits, bool has_len =true); verinum(V, unsigned nbits =1, bool has_len =true); verinum(uint64_t val, unsigned bits); + verinum(double val, bool dummy); verinum(const verinum&); // Create a signed number, with an unspecified number of bits. @@ -93,6 +94,8 @@ class verinum { signed long as_long() const; double as_double() const; string as_string() const; + private: + void signed_trim(); private: V* bits_; diff --git a/vpi/Makefile.in b/vpi/Makefile.in index f9b28a57d..a2b8bea94 100644 --- a/vpi/Makefile.in +++ b/vpi/Makefile.in @@ -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_time.o sys_vcd.o sys_vcdoff.o vcd_priv.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_LIBBZ2@,yes) diff --git a/vpi/sys_clog2.c b/vpi/sys_clog2.c new file mode 100644 index 000000000..043e44fd0 --- /dev/null +++ b/vpi/sys_clog2.c @@ -0,0 +1,109 @@ +/* + * 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 +#include +#include +#include +#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; + s_vpi_vecval vec; + (void) name; // Not used!/ + + /* Get the argument. */ + arg = vpi_scan(argv); + vpi_free_object(argv); + + vec = vpip_calc_clog2(arg); + + val.format = vpiVectorVal; + val.value.vector = &vec; + 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); +} diff --git a/vpi/sys_table.c b/vpi/sys_table.c index 4560744e7..56d480217 100644 --- a/vpi/sys_table.c +++ b/vpi/sys_table.c @@ -38,6 +38,7 @@ extern void sys_time_register(); extern void sys_vcd_register(); extern void sys_vcdoff_register(); extern void sys_special_register(); +extern void sys_clog2_register(); extern void vams_simparam_register(); #ifdef HAVE_LIBZ @@ -181,6 +182,7 @@ void (*vlog_startup_routines[])() = { sys_lxt_or_vcd_register, sys_sdf_register, sys_special_register, + sys_clog2_register, vams_simparam_register, 0 }; diff --git a/vpi/system.sft b/vpi/system.sft index d6ff0a311..75abe1bdc 100644 --- a/vpi/system.sft +++ b/vpi/system.sft @@ -14,6 +14,7 @@ $dist_poisson vpiSysFuncInt $dist_chi_square vpiSysFuncInt $dist_t vpiSysFuncInt $dist_erlang vpiSysFuncInt +$clog2 vpiSysFuncInt $simparam vpiSysFuncReal $simparam$str vpiSysFuncSized 1024 unsigned diff --git a/vpi_user.h b/vpi_user.h index 72b10c6c6..19d128cf8 100644 --- a/vpi_user.h +++ b/vpi_user.h @@ -566,6 +566,7 @@ extern DLLEXPORT void (*vlog_startup_routines[])(); buffer. The value must be a vpiStrengthVal. */ extern void vpip_format_strength(char*str, s_vpi_value*value, unsigned bit); extern void vpip_set_return_value(int value); +extern s_vpi_vecval vpip_calc_clog2(vpiHandle arg); EXTERN_C_END diff --git a/vvp/vpi_callback.cc b/vvp/vpi_callback.cc index 931d92d39..db8cf64b1 100644 --- a/vvp/vpi_callback.cc +++ b/vvp/vpi_callback.cc @@ -127,6 +127,12 @@ static struct __vpiCallback* make_value_change(p_cb_data data) obj->cb_time.type = vpiSuppressTime; } obj->cb_data.time = &obj->cb_time; + if (data->value) { + obj->cb_value = *(data->value); + } else { + obj->cb_value.format = vpiSuppressVal; + } + obj->cb_data.value = &obj->cb_value; assert(data->obj); assert(data->obj->vpi_type); @@ -457,8 +463,25 @@ void callback_execute(struct __vpiCallback*cur) vpi_mode_flag = VPI_MODE_RWSYNC; assert(cur->cb_data.cb_rtn); - cur->cb_data.time->type = vpiSimTime; - vpip_time_to_timestruct(cur->cb_data.time, schedule_simtime()); + switch (cur->cb_data.time->type) { + case vpiSimTime: + vpip_time_to_timestruct(cur->cb_data.time, schedule_simtime()); + break; + case vpiScaledRealTime: { + cur->cb_data.time->real = + vpip_time_to_scaled_real(schedule_simtime(), + (struct __vpiScope *) vpi_handle(vpiScope, + cur->cb_data.obj)); + break; + } + case vpiSuppressTime: + break; + default: + fprintf(stderr, "Unsupported time format %d.\n", + cur->cb_data.time->type); + assert(0); + break; + } (cur->cb_data.cb_rtn)(&cur->cb_data); vpi_mode_flag = save_mode; @@ -546,10 +569,32 @@ void vvp_fun_signal::get_value(struct t_vpi_value*vp) { switch (vp->format) { case vpiScalarVal: + // This works because vvp_bit4_t has the same encoding + // as a scalar value! See vpip_vec4_get_value() for a + // more robust method. vp->value.scalar = value(0); break; + + case vpiBinStrVal: + case vpiOctStrVal: + case vpiDecStrVal: + case vpiHexStrVal: + case vpiIntVal: + case vpiVectorVal: + case vpiStringVal: + case vpiRealVal: { + unsigned wid = size(); + vvp_vector4_t vec4(wid); + for (unsigned idx = 0; idx < wid; idx += 1) { + vec4.set_bit(idx, value(idx)); + } + vpip_vec4_get_value(vec4, wid, false, vp); + break; + } + case vpiSuppressVal: break; + default: fprintf(stderr, "vpi_callback: value " "format %d not supported (fun_signal)\n", @@ -622,6 +667,9 @@ void vvp_fun_signal_real::get_value(struct t_vpi_value*vp) break; } + case vpiSuppressVal: + break; + default: fprintf(stderr, "vpi_callback: value " "format %d not supported (fun_signal_real)\n", diff --git a/vvp/vpi_priv.cc b/vvp/vpi_priv.cc index 0c004d2a9..d7a897604 100644 --- a/vvp/vpi_priv.cc +++ b/vvp/vpi_priv.cc @@ -456,6 +456,13 @@ void vpip_vec4_get_value(const vvp_vector4_t&word_val, unsigned width, vp->format); assert(0 && "format not implemented"); + case vpiObjTypeVal: + vp->format = vpiVectorVal; + break; + + case vpiSuppressVal: + break; + case vpiBinStrVal: rbuf = need_result_buf(width+1, RBUF_VAL); for (unsigned idx = 0 ; idx < width ; idx += 1) { @@ -504,6 +511,7 @@ void vpip_vec4_get_value(const vvp_vector4_t&word_val, unsigned width, break; case BIT4_X: vp->value.scalar = vpiX; + break; case BIT4_Z: vp->value.scalar = vpiZ; break; @@ -1029,3 +1037,63 @@ extern "C" void vpi_control(PLI_INT32 operation, ...) vpi_sim_vcontrol(operation, ap); va_end(ap); } + +/* + * This routine calculated the return value for $clog2. + * It is easier to do it here vs trying to to use the VPI interface. + */ +extern "C" s_vpi_vecval vpip_calc_clog2(vpiHandle arg) +{ + s_vpi_vecval rtn; + s_vpi_value val; + vvp_vector4_t vec4; + bool is_neg = false; // At this point only a real can be negative. + + /* Get the value as a vvp_vector4_t. */ + val.format = vpiObjTypeVal; + vpi_get_value(arg, &val); + if (val.format == vpiRealVal) { + vpi_get_value(arg, &val); + /* All double values can be represented in 1024 bits. */ + vec4 = vvp_vector4_t(1024, val.value.real); + if (val.value.real < 0) is_neg = true; + } else { + val.format = vpiVectorVal; + vpi_get_value(arg, &val); + unsigned wid = vpi_get(vpiSize, arg); + vec4 = vvp_vector4_t(wid, BIT4_0); + for (unsigned idx=0; idx < wid; idx += 1) { + PLI_INT32 aval = val.value.vector[idx/32].aval; + PLI_INT32 bval = val.value.vector[idx/32].bval; + aval >>= idx % 32; + bval >>= idx % 32; + int bitmask = (aval&1) | ((bval<<1)&2); + vvp_bit4_t bit = scalar_to_bit4(bitmask); + vec4.set_bit(idx, bit); + } + } + + if (vec4.has_xz()) { + rtn.aval = rtn.bval = 0xFFFFFFFFU; /* Set to 'bx. */ + return rtn; + } + + vvp_vector2_t vec2(vec4); + + if (is_neg) vec2.trim_neg(); /* This is a special trim! */ + else vec2.trim(); /* This makes less work shifting. */ + + /* Calculate the clog2 result. */ + PLI_INT32 res = 0; + if (!vec2.is_zero()) { + vec2 -= vvp_vector2_t(1, vec2.size()); + while(!vec2.is_zero()) { + res += 1; + vec2 >>= 1; + } + } + + rtn.aval = res; + rtn.bval = 0; + return rtn; +} diff --git a/vvp/vpi_priv.h b/vvp/vpi_priv.h index 25a5282b8..49d38ed5d 100644 --- a/vvp/vpi_priv.h +++ b/vvp/vpi_priv.h @@ -137,6 +137,7 @@ struct __vpiCallback { // user supplied callback data struct t_cb_data cb_data; struct t_vpi_time cb_time; + struct t_vpi_value cb_value; // scheduled event struct sync_cb* cb_sync; diff --git a/vvp/vpi_signal.cc b/vvp/vpi_signal.cc index eacbf1732..cea4c9882 100644 --- a/vvp/vpi_signal.cc +++ b/vvp/vpi_signal.cc @@ -728,23 +728,6 @@ static vvp_vector4_t from_stringval(const char*str, unsigned wid) return val; } -static vvp_bit4_t scalar_to_bit4(PLI_INT32 scalar) -{ - switch(scalar) { - case vpi0: - return BIT4_0; - case vpi1: - return BIT4_1; - case vpiX: - return BIT4_X; - case vpiZ: - return BIT4_Z; - default: - fprintf(stderr, "Unsupported scalar value %d.\n", scalar); - assert(0); - } -} - static vpiHandle signal_put_value(vpiHandle ref, s_vpi_value*vp, int flags) { unsigned wid; diff --git a/vvp/vthread.cc b/vvp/vthread.cc index 2703bccb0..373afefca 100644 --- a/vvp/vthread.cc +++ b/vvp/vthread.cc @@ -2071,7 +2071,14 @@ bool of_FORK(vthread_t thr, vvp_code_t cp) thr->fork_count += 1; - schedule_vthread(child, 0, true); + /* If the new child was created to evaluate a function, + run it immediately, then return to this thread. */ + if (cp->scope->base.vpi_type->type_code == vpiFunction) { + child->is_scheduled = 1; + vthread_run(child); + } else { + schedule_vthread(child, 0, true); + } return true; } diff --git a/vvp/vvp.def b/vvp/vvp.def index ad3990f05..1ec96567d 100644 --- a/vvp/vvp.def +++ b/vvp/vvp.def @@ -37,3 +37,4 @@ vpi_vprintf vpip_format_strength vpip_set_return_value +vpip_calc_clog2 diff --git a/vvp/vvp_net.cc b/vvp/vvp_net.cc index d922bb77d..5719f068f 100644 --- a/vvp/vvp_net.cc +++ b/vvp/vvp_net.cc @@ -130,6 +130,23 @@ vvp_bit4_t add_with_carry(vvp_bit4_t a, vvp_bit4_t b, vvp_bit4_t&c) } } +vvp_bit4_t scalar_to_bit4(PLI_INT32 scalar) +{ + switch(scalar) { + case vpi0: + return BIT4_0; + case vpi1: + return BIT4_1; + case vpiX: + return BIT4_X; + case vpiZ: + return BIT4_Z; + default: + fprintf(stderr, "Unsupported scalar value %d.\n", scalar); + assert(0); + } +} + vvp_bit4_t operator ^ (vvp_bit4_t a, vvp_bit4_t b) { if (bit4_is_xz(a)) @@ -372,19 +389,12 @@ vvp_vector4_t::vvp_vector4_t(unsigned size, double val) double fraction; int exponent; - /* We return 'bx for a NaN. */ - if (val != val) { + /* We return 'bx for a NaN or +/- infinity. */ + if (val != val || (val && (val == 0.5*val))) { allocate_words_(size, WORD_X_ABITS, WORD_X_BBITS); return; } - /* We return 'b1 for + infinity or 'b0 for - infinity. */ - if (val && (val == 0.5*val)) { - if (val > 0) allocate_words_(size, WORD_1_ABITS, WORD_1_BBITS); - else allocate_words_(size, WORD_0_ABITS, WORD_0_BBITS); - return; - } - /* Convert to a positive result. */ if (val < 0.0) { is_neg = true; @@ -1618,6 +1628,15 @@ void vvp_vector2_t::trim() while (value(wid_-1) == 0 && wid_ > 1) wid_ -= 1; } +/* This is a special trim that is used on numbers we know represent a + * negative signed value (they came from a negative real value). */ +void vvp_vector2_t::trim_neg() +{ + if (value(wid_-1) == 1 && wid_ > 32) { + while (value(wid_-2) == 1 && wid_ > 32) wid_ -= 1; + } +} + int vvp_vector2_t::value(unsigned idx) const { if (idx >= wid_) diff --git a/vvp/vvp_net.h b/vvp/vvp_net.h index 6e6497179..1297564e8 100644 --- a/vvp/vvp_net.h +++ b/vvp/vvp_net.h @@ -20,6 +20,7 @@ */ # include "config.h" +# include "vpi_user.h" # include # include # include @@ -67,6 +68,8 @@ inline char vvp_bit4_to_ascii(vvp_bit4_t a) { return "01zx"[a]; } extern vvp_bit4_t add_with_carry(vvp_bit4_t a, vvp_bit4_t b, vvp_bit4_t&c); +extern vvp_bit4_t scalar_to_bit4(PLI_INT32 scalar); + /* Return TRUE if the bit is BIT4_X or BIT4_Z. The fast implementation here relies on the encoding of vvp_bit4_t values. */ inline bool bit4_is_xz(vvp_bit4_t a) { return a >= 2; } @@ -470,6 +473,9 @@ class vvp_vector2_t { void set_bit(unsigned idx, int bit); // Make the size just big enough to hold the first 1 bit. void trim(); + // Trim off extra 1 bit since this is representing a negative value. + // Always keep at least 32 bits. + void trim_neg(); private: enum { BITS_PER_WORD = 8 * sizeof(unsigned long) };