diff --git a/compiler.h b/compiler.h index 934344b74..94bf37c2c 100644 --- a/compiler.h +++ b/compiler.h @@ -134,6 +134,9 @@ extern bool gn_specify_blocks_flag; /* If this flag is true, then support/elaborate Verilog-AMS. */ extern bool gn_verilog_ams_flag; +/* If this flag is true, then support/elaborate SystemVerilog. */ +extern bool gn_system_verilog_flag; + /* If this flag is false a warning is printed when the port declaration is scalar and the net/register definition is vectored. */ extern bool gn_io_range_error_flag; diff --git a/driver/main.c b/driver/main.c index bf322d77d..f7a7574d0 100644 --- a/driver/main.c +++ b/driver/main.c @@ -281,7 +281,7 @@ static int t_version_only(void) } fflush(0); - snprintf(tmp, sizeof tmp, "%s%civl -V -C%s -C%s", pbase, sep, + snprintf(tmp, sizeof tmp, "%s%civl -V -C%s -C%s", base, sep, iconfig_path, iconfig_common_path); rc = system(tmp); if (rc != 0) { diff --git a/elab_scope.cc b/elab_scope.cc index afc438573..5929b03b2 100644 --- a/elab_scope.cc +++ b/elab_scope.cc @@ -174,6 +174,15 @@ static void elaborate_scope_parameters_(Design*des, NetScope*scope, for (mparm_it_t cur = parameters.begin() ; cur != parameters.end() ; cur ++) { + // A parameter can not have the same name as a genvar. + if (scope->find_genvar((*cur).first)) { + cerr << cur->second.get_fileline() + << ": error: parameter and genvar in '" + << scope->fullname() << "' have the same name '" + << (*cur).first << "'." << endl; + des->errors += 1; + } + elaborate_parm_item_((*cur).first, (*cur).second, des, scope); } } @@ -184,6 +193,15 @@ static void elaborate_scope_localparams_(Design*des, NetScope*scope, for (mparm_it_t cur = localparams.begin() ; cur != localparams.end() ; cur ++) { + // A localparam can not have the same name as a genvar. + if (scope->find_genvar((*cur).first)) { + cerr << cur->second.get_fileline() + << ": error: localparam and genvar in '" + << scope->fullname() << "' have the same name '" + << (*cur).first << "'." << endl; + des->errors += 1; + } + elaborate_parm_item_((*cur).first, (*cur).second, des, scope); } } @@ -249,6 +267,15 @@ static void elaborate_scope_tasks(Design*des, NetScope*scope, continue; } + // A task can not have the same name as a genvar. + if (scope->find_genvar((*cur).first)) { + cerr << cur->second->get_fileline() + << ": error: task and genvar in '" + << scope->fullname() << "' have the same name '" + << (*cur).first << "'." << endl; + des->errors += 1; + } + // A task can not have the same name as a parameter. const NetExpr *ex_msb, *ex_lsb; const NetExpr *parm = scope->get_parameter((*cur).first, ex_msb, @@ -296,6 +323,15 @@ static void elaborate_scope_funcs(Design*des, NetScope*scope, continue; } + // A function can not have the same name as a genvar. + if (scope->find_genvar((*cur).first)) { + cerr << cur->second->get_fileline() + << ": error: function and genvar in '" + << scope->fullname() << "' have the same name '" + << (*cur).first << "'." << endl; + des->errors += 1; + } + // A function can not have the same name as a parameter. const NetExpr *ex_msb, *ex_lsb; const NetExpr *parm = scope->get_parameter((*cur).first, ex_msb, @@ -361,6 +397,12 @@ bool Module::elaborate_scope(Design*des, NetScope*scope, << scope_path(scope) << "." << endl; } + // Add the genvars to the scope. + typedef map::const_iterator genvar_it_t; + for (genvar_it_t cur = genvars.begin(); cur != genvars.end(); cur++ ) { + scope->add_genvar((*cur).first, (*cur).second); + } + // Generate all the parameters that this instance of this // module introduces to the design. This loop elaborates the // parameters, but doesn't evaluate references to @@ -521,12 +563,13 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) { // Check that the loop_index variable was declared in a // genvar statement. - - // MISSING CODE! - // - // Also the genvar checks below need to be moved/changed - // when this is implemented. They currently work, but do - // not reference the genvar statement. + if (container->find_genvar(loop_index) == 0) { + cerr << get_fileline() << ": error: genvar is missing for " + "generate \"loop\" variable '" << loop_index << "'." + << endl; + des->errors += 1; + return false; + } // We're going to need a genvar... int genvar; @@ -558,6 +601,15 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) des->errors += 1; return false; } + + // A generate "loop" can not have the same name as a genvar. + if (container->find_genvar(scope_name)) { + cerr << get_fileline() << ": error: generate \"loop\" and " + "genvar in '" << container->fullname() + << "' have the same name '" << scope_name << "'." << endl; + des->errors += 1; + } + // A generate "loop" can not have the same name as a named event. const NetEvent *event = container->find_event(scope_name); if (event) { @@ -578,17 +630,11 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) des->errors += 1; } - // Since we will be adding the genvar value as a local parameter - // to each instances scope. We need to make sure a parameter does - // not already exist. - texpr = container->get_parameter(loop_index, tmsb, tlsb); - if (texpr != 0) { - cerr << get_fileline() << ": error: genvar and " - "parameter in '" << container->fullname() - << "' have the same name '" << loop_index << "'." << endl; - des->errors += 1; - return false; - } + // These have all been checked so we just need to skip the actual + // generation for these name conflicts. Not skipping these two will + // cause the compiler to have problems (assert, inf. loop, etc.). + if (container->get_parameter(loop_index, tmsb, tlsb)) return false; + if (container->find_event(loop_index)) return false; genvar = init->value().as_long(); delete init_ex; @@ -672,7 +718,7 @@ bool PGenerate::generate_scope_loop_(Design*des, NetScope*container) } // Clear the genvar_tmp field in the scope to reflect that the - // genvar is no longer value for evaluating expressions. + // genvar is no longer valid for evaluating expressions. container->genvar_tmp = perm_string(); return true; @@ -717,6 +763,14 @@ bool PGenerate::generate_scope_condit_(Design*des, NetScope*container, bool else return false; } + // A generate "if" can not have the same name as a genvar. + if (container->find_genvar(scope_name)) { + cerr << get_fileline() << ": error: generate \"if\" and " + "genvar in '" << container->fullname() + << "' have the same name '" << scope_name << "'." << endl; + des->errors += 1; + } + // A generate "if" can not have the same name as a named event. const NetEvent *event = container->find_event(scope_name); if (event) { @@ -855,6 +909,14 @@ bool PGenerate::generate_scope_case_(Design*des, NetScope*container) return false; } + // A generate "case" can not have the same name as a genvar. + if (container->find_genvar(item->scope_name)) { + cerr << get_fileline() << ": error: generate \"case\" and " + "genvar in '" << container->fullname() + << "' have the same name '" << use_name << "'." << endl; + des->errors += 1; + } + // A generate "case" can not have the same name as a named event. const NetEvent *event = container->find_event(item->scope_name); if (event) { @@ -895,7 +957,8 @@ bool PGenerate::generate_scope_case_(Design*des, NetScope*container) bool PGenerate::generate_scope_nblock_(Design*des, NetScope*container) { hname_t use_name (scope_name); - // A generate "case" can not have the same name as another scope object. + // A generate "block" can not have the same name as another scope + // object. const NetScope *child = container->child(use_name); if (child) { cerr << get_fileline() << ": error: generate \"block\" and "; @@ -906,6 +969,14 @@ bool PGenerate::generate_scope_nblock_(Design*des, NetScope*container) return false; } + // A generate "block" can not have the same name as a genvar. + if (container->find_genvar(scope_name)) { + cerr << get_fileline() << ": error: generate \"block\" and " + "genvar in '" << container->fullname() + << "' have the same name '" << scope_name << "'." << endl; + des->errors += 1; + } + // A generate "block" can not have the same name as a named event. const NetEvent *event = container->find_event(scope_name); if (event) { @@ -1057,6 +1128,14 @@ void PGModule::elaborate_scope_mod_(Design*des, Module*mod, NetScope*sc) const return; } + // A module instance can not have the same name as a genvar. + if (sc->find_genvar(get_name())) { + cerr << get_fileline() << ": error: module <" << mod->mod_name() + << "> instance and genvar in '" << sc->fullname() + << "' have the same name '" << get_name() << "'." << endl; + des->errors += 1; + } + // A module instance can not have the same name as a parameter. const NetExpr *ex_msb, *ex_lsb; const NetExpr *parm = sc->get_parameter(get_name(), ex_msb, ex_lsb); @@ -1314,6 +1393,14 @@ void PEvent::elaborate_scope(Design*des, NetScope*scope) const des->errors += 1; } + // A named event can not have the same name as a genvar. + if (scope->find_genvar(name_)) { + cerr << get_fileline() << ": error: named event and " + << "genvar in '" << scope->fullname() + << "' have the same name '" << name_ << "'." << endl; + des->errors += 1; + } + // A named event can not have the same name as a parameter. const NetExpr *ex_msb, *ex_lsb; const NetExpr *parm = scope->get_parameter(name_, ex_msb, ex_lsb); @@ -1416,6 +1503,14 @@ void PBlock::elaborate_scope(Design*des, NetScope*scope) const return; } + // A named block can not have the same name as a genvar. + if (scope->find_genvar(pscope_name())) { + cerr << get_fileline() << ": error: named block and " + "genvar in '" << scope->fullname() + << "' have the same name '" << use_name << "'." << endl; + des->errors += 1; + } + // A named block can not have the same name as a parameter. const NetExpr *ex_msb, *ex_lsb; const NetExpr *parm = scope->get_parameter(pscope_name(), ex_msb, diff --git a/elab_sig.cc b/elab_sig.cc index 752b2cb77..0c69c3b96 100644 --- a/elab_sig.cc +++ b/elab_sig.cc @@ -878,6 +878,14 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope) const cerr << " in '" << scope->fullname() << "' have the same name '" << name_ << "'." << endl; des->errors += 1; + } + // A signal can not have the same name as a genvar. + const LineInfo *genvar = scope->find_genvar(name_); + if (genvar) { + cerr << get_fileline() << ": error: signal and genvar in '" + << scope->fullname() << "' have the same name '" << name_ + << "'." << endl; + des->errors += 1; } // A signal can not have the same name as a parameter. const NetExpr *ex_msb, *ex_lsb; diff --git a/eval_tree.cc b/eval_tree.cc index 668213d62..deb313f34 100644 --- a/eval_tree.cc +++ b/eval_tree.cc @@ -1161,16 +1161,6 @@ NetEConst* NetEConcat::eval_tree(int prune_to_width) if (local_errors > 0) return 0; - // Handle the special case that the repeat expression is - // zero. In this case, just return a 0 value with the expected - // width. - if (repeat_val == 0) { - verinum val (verinum::V0, expr_width()); - NetEConst*res = new NetEConst(val); - res->set_width(val.len()); - return res; - } - // At this point, the "gap" is the width of a single repeat of // the concatenation. The total width of the result is the gap // times the repeat count. diff --git a/expr_synth.cc b/expr_synth.cc index 3affe9086..70b5dff76 100644 --- a/expr_synth.cc +++ b/expr_synth.cc @@ -722,25 +722,35 @@ NetNet* NetEConcat::synthesize(Design*des, NetScope*scope, NetExpr*root) unsigned nparms = parms_.count(); NetNet**tmp = new NetNet*[parms_.count()]; bool flag = true; + ivl_variable_type_t data_type = IVL_VT_NO_TYPE; for (unsigned idx = 0 ; idx < parms_.count() ; idx += 1) { if (parms_[idx]->expr_width() == 0) { - tmp[idx] = 0; + /* We need to synthesize a replication of zero. */ + tmp[idx] = parms_[idx]->synthesize(des, scope, root); + assert(tmp[idx] == 0); nparms -= 1; } else { tmp[idx] = parms_[idx]->synthesize(des, scope, root); if (tmp[idx] == 0) flag = false; + /* Set the data type to the first one found. */ + if (data_type == IVL_VT_NO_TYPE) { + data_type = tmp[idx]->data_type(); + } } } if (flag == false) return 0; - ivl_assert(*this, tmp[0]); + ivl_assert(*this, data_type != IVL_VT_NO_TYPE); + + /* If this is a replication of zero just return 0. */ + if (expr_width() == 0) return 0; /* Make a NetNet object to carry the output vector. */ perm_string path = scope->local_symbol(); NetNet*osig = new NetNet(scope, path, NetNet::IMPLICIT, expr_width()); osig->local_flag(true); - osig->data_type(tmp[0]->data_type()); + osig->data_type(data_type); NetConcat*concat = new NetConcat(scope, scope->local_symbol(), osig->vector_width(), diff --git a/ivlpp/lexor.lex b/ivlpp/lexor.lex index e96ecf3e7..2a34f8c74 100644 --- a/ivlpp/lexor.lex +++ b/ivlpp/lexor.lex @@ -457,7 +457,7 @@ keywords (include|define|undef|ifdef|ifndef|else|elseif|endif) \n\r | \r\n | \n | -\r { istack->lineno += 1; } +\r { istack->lineno += 1; fputc('\n', yyout); } "*/" { BEGIN(comment_enter); } [^\r\n] { } diff --git a/lexor.lex b/lexor.lex index 954a2ab61..9e8504c66 100644 --- a/lexor.lex +++ b/lexor.lex @@ -107,6 +107,8 @@ W [ \t\b\f\r]+ S [afpnumkKMGT] +TU [munpf] + %% /* Recognize the various line directives. */ @@ -248,6 +250,15 @@ S [afpnumkKMGT] in_UDP = false; break; + /* Translate these to checks if we already have or are + * outside the declaration region. */ + case K_timeunit: + if (have_timeunit_decl) rc = K_timeunit_check; + break; + case K_timeprecision: + if (have_timeprec_decl) rc = K_timeprecision_check; + break; + default: yylval.text = 0; break; @@ -323,6 +334,13 @@ S [afpnumkKMGT] based_size = yylval.number->as_ulong(); return DEC_NUMBER; } + /* This rule handles scaled time values for SystemVerilog. */ +[0-9][0-9_]*(\.[0-9][0-9_]*)?{TU}?s { + if(gn_system_verilog_flag) { + yylval.text = strdupnew(yytext); + return TIME_LITERAL; + } else REJECT; } + /* These rules handle the scaled real literals from Verilog-AMS. The value is a number with a single letter scale factor. If verilog-ams is not enabled, then reject this rule. If it is @@ -1205,6 +1223,13 @@ static void process_timescale(const char*txt) return; } + /* The time unit must be greater than or equal to the precision. */ + if (unit < prec) { + VLerror(yylloc, "error: `timescale unit must not be less than " + "the precision."); + return; + } + pform_set_timescale(unit, prec, yylloc.text, yylloc.first_line); } diff --git a/lexor_keyword.gperf b/lexor_keyword.gperf index 5ba577a71..9602f952a 100644 --- a/lexor_keyword.gperf +++ b/lexor_keyword.gperf @@ -162,6 +162,8 @@ tan, GN_KEYWORDS_VAMS_2_3, K_tan tanh, GN_KEYWORDS_VAMS_2_3, K_tanh task, GN_KEYWORDS_1364_1995, K_task time, GN_KEYWORDS_1364_1995, K_time +timeprecision, GN_KEYWORDS_1364_1995, K_timeprecision +timeunit, GN_KEYWORDS_1364_1995, K_timeunit tran, GN_KEYWORDS_1364_1995, K_tran tranif0, GN_KEYWORDS_1364_1995, K_tranif0 tranif1, GN_KEYWORDS_1364_1995, K_tranif1 diff --git a/net_expr.cc b/net_expr.cc index dee99b45d..3b5890caa 100644 --- a/net_expr.cc +++ b/net_expr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002-2008 Stephen Williams (steve@icarus.com) + * Copyright (c) 2002-2009 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 @@ -257,15 +257,13 @@ ivl_variable_type_t NetEBMinMax::expr_type() const NetEBMult::NetEBMult(char op__, NetExpr*l, NetExpr*r) : NetEBinary(op__, l, r) { - if (expr_type() == IVL_VT_REAL) + if (expr_type() == IVL_VT_REAL) { expr_width(1); - else - expr_width(l->expr_width() + r->expr_width()); - - if (expr_type() == IVL_VT_REAL) cast_signed(true); - else + } else { + expr_width(l->expr_width() + r->expr_width()); cast_signed(l->has_sign() && r->has_sign()); + } } NetEBMult::~NetEBMult() @@ -294,8 +292,8 @@ NetEBPow::NetEBPow(char op__, NetExpr*l, NetExpr*r) : NetEBinary(op__, l, r) { assert(op__ == 'p'); - /* This is incorrect! a * (2^b - 1) is close. */ - expr_width(l->expr_width()+r->expr_width()); + /* You could need up to a * (2^b - 1) bits. */ + expr_width(l->expr_width()); cast_signed(l->has_sign() || r->has_sign()); } @@ -438,6 +436,7 @@ NetECReal::NetECReal(const verireal&val) : value_(val) { expr_width(1); + cast_signed(true); } NetECReal::~NetECReal() diff --git a/net_scope.cc b/net_scope.cc index 3a2e6f91b..6bdcd8713 100644 --- a/net_scope.cc +++ b/net_scope.cc @@ -395,6 +395,30 @@ NetEvent* NetScope::find_event(perm_string name) return 0; } +// We only add genvars to a module scope, so we do not need to search +// for the module scope here. +void NetScope::add_genvar(perm_string name, LineInfo *li) +{ + genvars_[name] = li; +} + +LineInfo* NetScope::find_genvar(perm_string name) +{ + // genvars are only added to the module so we need to find it + // if we are in a sub scope. + NetScope *scope = this; + while (scope->type() != NetScope::MODULE) { + scope = scope->parent(); + assert(scope != NULL); + } + + if (scope->genvars_.find(name) != scope->genvars_.end()) { + return scope->genvars_[name]; + } + + return 0; +} + void NetScope::add_signal(NetNet*net) { signals_map_[net->name()]=net; diff --git a/netlist.h b/netlist.h index 05c57ebfd..d34fca4c5 100644 --- a/netlist.h +++ b/netlist.h @@ -720,6 +720,9 @@ class NetScope : public Attrib { void rem_event(NetEvent*); NetEvent*find_event(perm_string name); + /* These methods add or find a genvar that lives in this scope. */ + void add_genvar(perm_string name, LineInfo *li); + LineInfo* find_genvar(perm_string name); /* These methods manage signals. The add_ and rem_signal methods are used by the NetNet objects to make themselves @@ -899,6 +902,8 @@ class NetScope : public Attrib { NetEvent *events_; + map genvars_; + typedef std::map::const_iterator signals_map_iter_t; std::map signals_map_; perm_string module_name_; diff --git a/parse.y b/parse.y index 44f09da35..592115727 100644 --- a/parse.y +++ b/parse.y @@ -35,6 +35,9 @@ class PSpecPath; extern void lex_start_table(); extern void lex_end_table(); +bool have_timeunit_decl = false; +bool have_timeprec_decl = false; + static svector* param_active_range = 0; static bool param_active_signed = false; static ivl_variable_type_t param_active_type = IVL_VT_LOGIC; @@ -218,7 +221,7 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2) list *dimensions; }; -%token IDENTIFIER SYSTEM_IDENTIFIER STRING +%token IDENTIFIER SYSTEM_IDENTIFIER STRING TIME_LITERAL %token DISCIPLINE_IDENTIFIER %token PATHPULSE_IDENTIFIER %token BASED_NUMBER DEC_NUMBER @@ -269,7 +272,10 @@ static PECallFunction*make_call_function(perm_string tn, PExpr*arg1, PExpr*arg2) %token K_wone K_uwire /* The new tokens from 1800-2005. */ -%token K_always_comb K_always_ff K_always_latch K_assert +%token K_always_comb K_always_ff K_always_latch K_assert +%token K_timeprecision K_timeunit + /* Fake tokens that are passed once we have an initial token. */ +%token K_timeprecision_check K_timeunit_check /* The new tokens for Verilog-AMS 2.3. */ %token K_abs K_abstol K_access K_acos K_acosh K_analog K_asin K_asinh @@ -728,6 +734,10 @@ description delete[] $3; delete[] $5; } + | K_timeunit TIME_LITERAL ';' + { pform_set_timeunit($2, false, false); } + | K_timeprecision TIME_LITERAL ';' + { pform_set_timeprecision($2, false, false); } ; /* The discipline and nature declarations used to take no ';' after @@ -1975,6 +1985,25 @@ cont_assign_list { $$ = $1; } ; + /* We allow zero, one or two unique declarations. */ +local_timeunit_prec_decl_opt + : /* Empty */ + | local_timeunit_prec_decl + | local_timeunit_prec_decl local_timeunit_prec_decl + ; + + /* By setting the appropriate have_time???_decl we allow only + one declaration of each type in this module. */ +local_timeunit_prec_decl + : K_timeunit TIME_LITERAL ';' + { pform_set_timeunit($2, true, false); + have_timeunit_decl = true; + } + | K_timeprecision TIME_LITERAL ';' + { pform_set_timeprecision($2, true, false); + have_timeprec_decl = true; + } + ; /* This is the global structure of a module. A module in a start section, with optional ports, then an optional list of module @@ -1986,7 +2015,12 @@ module : attribute_list_opt module_start IDENTIFIER module_port_list_opt module_attribute_foreign ';' { pform_module_set_ports($6); } - module_item_list_opt + local_timeunit_prec_decl_opt + { have_timeunit_decl = true; // Every thing past here is + have_timeprec_decl = true; // a check! + pform_check_timeunit_prec(); + } + module_item_list_opt K_endmodule { Module::UCDriveType ucd; switch (uc_drive) { @@ -2003,8 +2037,9 @@ module : attribute_list_opt module_start IDENTIFIER } pform_endmodule($3, in_celldefine, ucd); delete[]$3; + have_timeunit_decl = false; // We will allow decls again. + have_timeprec_decl = false; } - ; module_start : K_module | K_macromodule ; @@ -2463,6 +2498,11 @@ module_item } | KK_attribute '(' error ')' ';' { yyerror(@1, "error: Malformed $attribute parameter list."); } + + | K_timeunit_check TIME_LITERAL ';' + { pform_set_timeunit($2, true, true); } + | K_timeprecision_check TIME_LITERAL ';' + { pform_set_timeprecision($2, true, true); } ; automatic_opt diff --git a/parse_misc.h b/parse_misc.h index 8b58aa4e8..c49cb1652 100644 --- a/parse_misc.h +++ b/parse_misc.h @@ -70,5 +70,11 @@ extern unsigned long based_size; extern bool in_celldefine; enum UCDriveType { UCD_NONE, UCD_PULL0, UCD_PULL1 }; extern UCDriveType uc_drive; +/* + * Flags to control if we are declaring or checking a timeunit or + * timeprecision statement. + */ +extern bool have_timeunit_decl; +extern bool have_timeprec_decl; #endif diff --git a/pform.cc b/pform.cc index 1e881c80f..9c2660f46 100644 --- a/pform.cc +++ b/pform.cc @@ -73,6 +73,21 @@ static NetNet::Type pform_default_nettype = NetNet::WIRE; static int pform_time_unit; static int pform_time_prec; +/* These two flags check the initial timeprecision and timeunit + * declaration inside a module. + */ +static bool tp_decl_flag = false; +static bool tu_decl_flag = false; + +/* + * Flags used to set time_from_timescale based on timeunit and + * timeprecision. + */ +static bool tu_global_flag = false; +static bool tp_global_flag = false; +static bool tu_local_flag = false; +static bool tp_local_flag = false; + static char*pform_timescale_file = 0; static unsigned pform_timescale_line; @@ -116,7 +131,7 @@ PTask* pform_push_task_scope(const struct vlltype&loc, char*name, bool is_auto) if (pform_cur_generate->tasks.find(task->pscope_name()) != pform_cur_generate->tasks.end()) { cerr << task->get_fileline() << ": error: duplicate " - " definition for task '" << name << "' in '" + "definition for task '" << name << "' in '" << pform_cur_module->mod_name() << "' (generate)." << endl; error_count += 1; @@ -292,6 +307,9 @@ void pform_set_timescale(int unit, int prec, assert(unit >= prec); pform_time_unit = unit; pform_time_prec = prec; + /* A `timescale clears the timeunit/timeprecision state. */ + tu_global_flag = false; + tp_global_flag = false; if (pform_timescale_file) { free(pform_timescale_file); @@ -339,6 +357,139 @@ void pform_set_timescale(int unit, int prec, } } +/* + * Get a timeunit or timeprecision value from a string. This is + * similar to the code in lexor.lex for the `timescale directive. + */ +static bool get_time_unit_prec(const char*cp, int &res, bool is_unit) +{ + /* We do not support a '_' in these time constants. */ + if (strchr(cp, '_')) { + if (is_unit) { + VLerror(yylloc, "Invalid timeunit constant ('_' is not " + "supported)."); + } else { + VLerror(yylloc, "Invalid timeprecision constant ('_' is not " + "supported)."); + } + return true; + } + + /* Check for the 1 digit. */ + if (*cp != '1') { + if (is_unit) { + VLerror(yylloc, "Invalid timeunit constant (1st digit)."); + } else { + VLerror(yylloc, "Invalid timeprecision constant (1st digit)."); + } + return true; + } + cp += 1; + + /* Check the number of zeros after the 1. */ + res = strspn(cp, "0"); + if (res > 2) { + if (is_unit) { + VLerror(yylloc, "Invalid timeunit constant (number of " + "zeros)."); + } else { + VLerror(yylloc, "Invalid timeprecision constant (number of " + "zeros)."); + } + return true; + } + cp += res; + + /* Now process the scaling string. */ + if (strncmp("s", cp, 1) == 0) { + res -= 0; + return false; + + } else if (strncmp("ms", cp, 2) == 0) { + res -= 3; + return false; + + } else if (strncmp("us", cp, 2) == 0) { + res -= 6; + return false; + + } else if (strncmp("ns", cp, 2) == 0) { + res -= 9; + return false; + + } else if (strncmp("ps", cp, 2) == 0) { + res -= 12; + return false; + + } else if (strncmp("fs", cp, 2) == 0) { + res -= 15; + return false; + + } + + ostringstream msg; + msg << "Invalid "; + if (is_unit) msg << "timeunit"; + else msg << "timeprecision"; + msg << " scale '" << cp << "'."; + VLerror(msg.str().c_str()); + return true; +} + +void pform_set_timeunit(const char*txt, bool in_module, bool only_check) +{ + int val; + + if (get_time_unit_prec(txt, val, true)) return; + + if (in_module) { + if (!only_check) { + pform_cur_module->time_unit = val; + tu_decl_flag = true; + tu_local_flag = true; + } else if (!tu_decl_flag) { + VLerror(yylloc, "error: repeat timeunit found and the " + "initial module timeunit is missing."); + return; + } else if (pform_cur_module->time_unit != val) { + VLerror(yylloc, "error: repeat timeunit does not match " + "the initial module timeunit " + "declaration."); + return; + } + + } else { + tu_global_flag = true; + pform_time_unit = val; + } +} + +void pform_set_timeprecision(const char*txt, bool in_module, bool only_check) +{ + int val; + + if (get_time_unit_prec(txt, val, false)) return; + + if (in_module) { + if (!only_check) { + pform_cur_module->time_precision = val; + tp_decl_flag = true; + tp_local_flag = true; + } else if (!tp_decl_flag) { + VLerror(yylloc, "error: repeat timeprecision found and the " + "initial module timeprecision is missing."); + return; + } else if (pform_cur_module->time_precision != val) { + VLerror(yylloc, "error: repeat timeprecision does not match " + "the initial module timeprecision " + "declaration."); + return; + } + } else { + pform_time_prec = val; + tp_global_flag=true; + } +} verinum* pform_verinum_with_size(verinum*siz, verinum*val, const char*file, unsigned lineno) @@ -397,8 +548,12 @@ void pform_startmodule(const char*name, const char*file, unsigned lineno, perm_string lex_name = lex_strings.make(name); pform_cur_module = new Module(lex_name); + /* Set the local time unit/precision to the global value. */ pform_cur_module->time_unit = pform_time_unit; pform_cur_module->time_precision = pform_time_prec; + tu_local_flag = tu_global_flag; + tp_local_flag = tp_global_flag; + /* If we have a timescale file then the time information is from * a timescale directive. */ pform_cur_module->time_from_timescale = pform_timescale_file != 0; @@ -431,6 +586,22 @@ void pform_startmodule(const char*name, const char*file, unsigned lineno, } } +/* + * In SystemVerilog we can have separate timeunit and timeprecision + * declarations. We need to have the values worked out by time this + * task is called. + */ +void pform_check_timeunit_prec() +{ + assert(pform_cur_module); + if (gn_system_verilog_flag && (pform_cur_module->time_unit < + pform_cur_module->time_precision)) { + VLerror("error: a timeprecision is missing or is too " + "large!"); + } else assert(pform_cur_module->time_unit >= + pform_cur_module->time_precision); +} + /* * This function is called by the parser to make a simple port * reference. This is a name without a .X(...), so the internal name @@ -471,6 +642,9 @@ void pform_endmodule(const char*name, bool in_celldefine, Module::UCDriveType uc_drive) { assert(pform_cur_module); + pform_cur_module->time_from_timescale = (tu_local_flag && + tp_local_flag) || + (pform_timescale_file != 0); perm_string mod_name = pform_cur_module->mod_name(); assert(strcmp(name, mod_name) == 0); pform_cur_module->is_cell = in_celldefine; @@ -495,6 +669,10 @@ void pform_endmodule(const char*name, bool in_celldefine, ivl_assert(*pform_cur_module, lexical_scope == 0); pform_cur_module = 0; + tp_decl_flag = false; + tu_decl_flag = false; + tu_local_flag = false; + tp_local_flag = false; } void pform_genvars(const struct vlltype&li, list*names) @@ -503,7 +681,14 @@ void pform_genvars(const struct vlltype&li, list*names) for (cur = names->begin(); cur != names->end() ; *cur++) { LineInfo*lni = new LineInfo(); FILE_NAME(lni, li); - pform_cur_module->genvars[*cur] = lni; + if (pform_cur_module->genvars.find(*cur) != + pform_cur_module->genvars.end()) { + cerr << lni->get_fileline() << ": error: duplicate " + "definition for genvar '" << *cur << "' in '" + << pform_cur_module->mod_name() << "'." << endl; + error_count += 1; + delete lni; + } else pform_cur_module->genvars[*cur] = lni; } delete names; diff --git a/pform.h b/pform.h index 53e56f38e..778d748d3 100644 --- a/pform.h +++ b/pform.h @@ -143,6 +143,7 @@ extern PWire* pform_get_wire_in_scope(perm_string name); */ extern void pform_startmodule(const char*, const char*file, unsigned lineno, svector*attr); +extern void pform_check_timeunit_prec(); extern void pform_module_set_ports(vector*); /* This function is used to support the port definition in a @@ -409,4 +410,11 @@ extern PExpr* pform_make_branch_probe_expression(const struct vlltype&loc, extern PExpr* pform_make_branch_probe_expression(const struct vlltype&loc, char*name, char*branch); +/* + * Tasks to set the timeunit or timeprecision for SystemVerilog. + */ +extern void pform_set_timeunit(const char*txt, bool in_module, bool only_check); +extern void pform_set_timeprecision(const char*txt, bool in_module, + bool only_check); + #endif diff --git a/t-dll-expr.cc b/t-dll-expr.cc index 303449a07..128830a44 100644 --- a/t-dll-expr.cc +++ b/t-dll-expr.cc @@ -199,7 +199,7 @@ void dll_target::expr_concat(const NetEConcat*net) cur->type_ = IVL_EX_CONCAT; cur->value_ = IVL_VT_VECTOR; cur->width_ = net->expr_width(); - cur->signed_ = 0; + cur->signed_ = net->has_sign() ? 1 : 0; cur->u_.concat_.rept = net->repeat(); cur->u_.concat_.parms = net->nparms(); diff --git a/tgt-vvp/eval_expr.c b/tgt-vvp/eval_expr.c index cd8bb004a..3e6074794 100644 --- a/tgt-vvp/eval_expr.c +++ b/tgt-vvp/eval_expr.c @@ -1619,82 +1619,118 @@ static struct vector_info draw_binary_expr(ivl_expr_t exp, static struct vector_info draw_concat_expr(ivl_expr_t exp, unsigned wid, int stuff_ok_flag) { - unsigned off, rep; + unsigned off, rep, expr_wid, concat_wid, num_sube, idx; struct vector_info res; int alloc_exclusive = (stuff_ok_flag&STUFF_OK_RO) ? 0 : 1; - /* Allocate a vector to hold the result. */ - res.base = allocate_vector(wid); - res.wid = wid; - if (res.base == 0) { - fprintf(stderr, "%s:%u: vvp.tgt error: " - "Unable to allocate %u thread bits " - "for result of concatenation.\n", - ivl_expr_file(exp), ivl_expr_lineno(exp), wid); - vvp_errors += 1; + /* Find out how wide the base concatenation expression is. */ + num_sube = ivl_expr_parms(exp); + expr_wid = 0; + for (idx = 0 ; idx < num_sube; idx += 1) { + expr_wid += ivl_expr_width(ivl_expr_parm(exp, idx)); } /* Get the repeat count. This must be a constant that has been evaluated at compile time. The operands will be repeated to form the result. */ rep = ivl_expr_repeat(exp); - off = 0; - while (rep > 0) { + /* Allocate a vector to hold the result. */ + if (rep == 0) { + /* If the replication is zero we need to allocate temporary + * space to build the concatenation. */ + res.base = allocate_vector(expr_wid); + res.wid = expr_wid; + } else { + res.base = allocate_vector(wid); + res.wid = wid; + } + if (res.base == 0) { + fprintf(stderr, "%s:%u: vvp.tgt error: " + "Unable to allocate %u thread bits " + "for result of concatenation.\n", + ivl_expr_file(exp), ivl_expr_lineno(exp), + rep ? wid : expr_wid); + vvp_errors += 1; + } - /* Each repeat, evaluate the sub-expressions, from lsb - to msb, and copy each into the result vector. The - expressions are arranged in the concatenation from - MSB to LSB, to go through them backwards. - - Abort the loop if the result vector gets filled up. */ - - unsigned idx = ivl_expr_parms(exp); - while ((idx > 0) && (off < wid)) { + /* If the result is the right size we can just build this in place. */ + concat_wid = rep*expr_wid; + if (concat_wid <= wid) { + off = 0; + /* Evaluate the base expression. */ + for (idx = num_sube; idx > 0; idx -= 1) { ivl_expr_t arg = ivl_expr_parm(exp, idx-1); unsigned awid = ivl_expr_width(arg); - - unsigned trans; struct vector_info avec; + assert(awid+off <= expr_wid); + /* Try to locate the subexpression in the - lookaside map. */ + * lookaside map and use it when available. */ avec.base = allocate_vector_exp(arg, awid, alloc_exclusive); avec.wid = awid; - trans = awid; - if ((off + awid) > wid) - trans = wid - off; - if (avec.base != 0) { assert(awid == avec.wid); - - fprintf(vvp_out, " %%mov %u, %u, %u; Reuse calculated expression\n", - res.base+off, - avec.base, trans); - clr_vector(avec); - + fprintf(vvp_out, " %%mov %u, %u, %u; Reuse " + "calculated expression.\n", + res.base+off, avec.base, awid); + clr_vector(avec); } else { struct vector_info dest; dest.base = res.base+off; - dest.wid = trans; + dest.wid = awid; draw_eval_expr_dest(arg, dest, 0); } - - idx -= 1; - off += trans; - assert(off <= wid); + off += awid; } - rep -= 1; - } - /* Pad the result with 0, if necessary. */ - if (off < wid) { - fprintf(vvp_out, " %%mov %u, 0, %u;\n", - res.base+off, wid-off); + /* Now repeat the expression as needed. */ + if (rep != 0) { + rep -= 1; + } else { + /* Clear the temporary space and return nothing. + * This will be caught in draw_eval_expr_dest() + * and dropped. */ + clr_vector(res); + res.base = 0; + res.wid = 0; + } + while (rep > 0) { + fprintf(vvp_out, " %%mov %u, %u, %u; Repetition %u\n", + res.base+expr_wid*rep, res.base, expr_wid, + rep+1); + rep -= 1; + } + + /* Pad the expression when needed. */ + if (wid > concat_wid) { + /* We can get a signed concatenation with $signed({...}). */ + if (ivl_expr_signed(exp)) { + unsigned base = res.base+concat_wid-1; + for (idx = 1; idx <= wid-concat_wid; idx += 1) { + fprintf(vvp_out, " %%mov %u, %u, 1;\n", + base+idx, base); + } + } else { + fprintf(vvp_out, " %%mov %u, 0, %u;\n", + res.base+concat_wid, wid-concat_wid); + } + } + } else { + /* The concatenation is too big for the result so draw it + * at full width and then copy the bits that are needed. */ + struct vector_info full_res; + full_res = draw_concat_expr(exp, concat_wid, stuff_ok_flag); + assert(full_res.base); + + fprintf(vvp_out, " %%mov %u, %u, %u;\n", res.base, + full_res.base, wid); + clr_vector(full_res); } /* Save the accumulated result in the lookaside map. */ @@ -2843,8 +2879,9 @@ static void draw_eval_expr_dest(ivl_expr_t exp, struct vector_info dest, tmp = draw_eval_expr_wid(exp, dest.wid, stuff_ok_flag); assert(tmp.wid == dest.wid); - fprintf(vvp_out, " %%mov %u, %u, %u;\n", - dest.base, tmp.base, dest.wid); + /* If the replication is 0 we can have a zero width, so skip it. */ + if (dest.wid) fprintf(vvp_out, " %%mov %u, %u, %u;\n", + dest.base, tmp.base, dest.wid); if (tmp.base >= 8) save_expression_lookaside(tmp.base, exp, tmp.wid); diff --git a/vpi/sys_scanf.c b/vpi/sys_scanf.c index f500aa585..b523d2d60 100644 --- a/vpi/sys_scanf.c +++ b/vpi/sys_scanf.c @@ -240,6 +240,14 @@ static int scan_format(vpiHandle callh, struct byte_source*src, vpiHandle argv) vpi_get_value(item, &val); fmtp = fmt = strdup(val.value.str); + /* See if we are at EOF before we even start. */ + ch = byte_getc(src); + if (ch == EOF) { + rc = EOF; + match_fail = 1; + } + byte_ungetc(src, ch); + while ( fmtp && *fmtp != 0 && !match_fail) { if (isspace(*fmtp)) { diff --git a/vvp/array.cc b/vvp/array.cc index 24fe3d21c..1f11b64ce 100644 --- a/vvp/array.cc +++ b/vvp/array.cc @@ -152,11 +152,25 @@ struct __vpiArrayVthrA { } }; -/* Get the array word size. This has only been checked for reg arrays. */ +/* Get the array word size. */ unsigned get_array_word_size(vvp_array_t array) { - assert(array->vals4); - return array->vals_width; + unsigned width; + + assert(array->array_count > 0); + /* For a net array we need to get the width from the first element. */ + if (array->nets) { + assert(array->vals4 == 0 && array->valsr == 0); + struct __vpiSignal*vsig = vpip_signal_from_handle(array->nets[0]); + assert(vsig); + width = vpip_size(vsig); + /* For a variable array we can get the width from vals_width. */ + } else { + assert(array->vals4 || array->valsr); + width = array->vals_width; + } + + return width; } bool is_net_array(vpiHandle obj) @@ -665,7 +679,7 @@ static int vpi_array_vthr_A_get(int code, vpiHandle ref) return 0; // Not implemented for now! case vpiSize: - return parent->vals_width; + return get_array_word_size(parent); case vpiLeftRange: return parent->msb.value; @@ -746,8 +760,8 @@ static void vpi_array_vthr_A_get_value(vpiHandle ref, p_vpi_value value) vpip_real_get_value(tmp, value); } else { vvp_vector4_t tmp = array_get_word(parent, index); - vpip_vec4_get_value(tmp, parent->vals_width, parent->signed_flag, - value); + unsigned width = get_array_word_size(parent); + vpip_vec4_get_value(tmp, width, parent->signed_flag, value); } } @@ -766,7 +780,8 @@ static vpiHandle vpi_array_vthr_A_put_value(vpiHandle ref, p_vpi_value vp, int) double val = real_from_vpi_value(vp); array_set_word(parent, index, val); } else { - vvp_vector4_t val = vec4_from_vpi_value(vp, parent->vals_width); + unsigned width = get_array_word_size(parent); + vvp_vector4_t val = vec4_from_vpi_value(vp, width); array_set_word(parent, index, 0, val); } diff --git a/vvp/event.cc b/vvp/event.cc index 8e0982ca0..4f4098b62 100644 --- a/vvp/event.cc +++ b/vvp/event.cc @@ -195,6 +195,8 @@ struct vvp_fun_edge_state_s : public waitable_state_s { vvp_fun_edge::vvp_fun_edge(edge_t e) : edge_(e) { + for (unsigned idx = 0 ; idx < 4 ; idx += 1) + bits_[idx] = BIT4_X; } vvp_fun_edge::~vvp_fun_edge() @@ -220,8 +222,6 @@ bool vvp_fun_edge::recv_vec4_(vvp_net_ptr_t port, const vvp_vector4_t&bit, vvp_fun_edge_sa::vvp_fun_edge_sa(edge_t e) : vvp_fun_edge(e), threads_(0) { - for (unsigned idx = 0 ; idx < 4 ; idx += 1) - bits_[idx] = BIT4_X; } vvp_fun_edge_sa::~vvp_fun_edge_sa() @@ -259,6 +259,7 @@ vvp_fun_edge_aa::~vvp_fun_edge_aa() void vvp_fun_edge_aa::alloc_instance(vvp_context_t context) { vvp_set_context_item(context, context_idx_, new vvp_fun_edge_state_s); + reset_instance(context); } void vvp_fun_edge_aa::reset_instance(vvp_context_t context) @@ -268,7 +269,7 @@ void vvp_fun_edge_aa::reset_instance(vvp_context_t context) state->threads = 0; for (unsigned idx = 0 ; idx < 4 ; idx += 1) - state->bits[idx] = BIT4_X; + state->bits[idx] = bits_[idx]; } #ifdef CHECK_WITH_VALGRIND @@ -308,6 +309,7 @@ void vvp_fun_edge_aa::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, recv_vec4(port, bit, context); context = vvp_get_next_context(context); } + bits_[port.port()] = bit.value(0); } } @@ -324,6 +326,8 @@ struct vvp_fun_anyedge_state_s : public waitable_state_s { vvp_fun_anyedge::vvp_fun_anyedge() { + for (unsigned idx = 0 ; idx < 4 ; idx += 1) + bitsr_[idx] = 0.0; } vvp_fun_anyedge::~vvp_fun_anyedge() @@ -369,8 +373,6 @@ bool vvp_fun_anyedge::recv_real_(vvp_net_ptr_t port, double bit, vvp_fun_anyedge_sa::vvp_fun_anyedge_sa() : threads_(0) { - for (unsigned idx = 0 ; idx < 4 ; idx += 1) - bitsr_[idx] = 0.0; } vvp_fun_anyedge_sa::~vvp_fun_anyedge_sa() @@ -416,6 +418,7 @@ vvp_fun_anyedge_aa::~vvp_fun_anyedge_aa() void vvp_fun_anyedge_aa::alloc_instance(vvp_context_t context) { vvp_set_context_item(context, context_idx_, new vvp_fun_anyedge_state_s); + reset_instance(context); } void vvp_fun_anyedge_aa::reset_instance(vvp_context_t context) @@ -425,8 +428,8 @@ void vvp_fun_anyedge_aa::reset_instance(vvp_context_t context) state->threads = 0; for (unsigned idx = 0 ; idx < 4 ; idx += 1) { - state->bits[idx].set_to_x(); - state->bitsr[idx] = 0.0; + state->bits[idx] = bits_[idx]; + state->bitsr[idx] = bitsr_[idx]; } } @@ -467,6 +470,7 @@ void vvp_fun_anyedge_aa::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, recv_vec4(port, bit, context); context = vvp_get_next_context(context); } + bits_[port.port()] = bit; } } @@ -487,6 +491,7 @@ void vvp_fun_anyedge_aa::recv_real(vvp_net_ptr_t port, double bit, recv_real(port, bit, context); context = vvp_get_next_context(context); } + bitsr_[port.port()] = bit; } } diff --git a/vvp/event.h b/vvp/event.h index 7700f4329..fd7ac81ae 100644 --- a/vvp/event.h +++ b/vvp/event.h @@ -141,6 +141,8 @@ class vvp_fun_edge : public vvp_net_fun_t, public waitable_hooks_s { bool recv_vec4_(vvp_net_ptr_t port, const vvp_vector4_t&bit, vvp_bit4_t&old_bit, vthread_t&threads); + vvp_bit4_t bits_[4]; + private: edge_t edge_; }; @@ -165,7 +167,6 @@ class vvp_fun_edge_sa : public vvp_fun_edge { private: vthread_t threads_; - vvp_bit4_t bits_[4]; }; /* @@ -214,6 +215,10 @@ class vvp_fun_anyedge : public vvp_net_fun_t, public waitable_hooks_s { vvp_vector4_t&old_bits, vthread_t&threads); bool recv_real_(vvp_net_ptr_t port, double bit, double&old_bits, vthread_t&threads); + + vvp_vector4_t bits_[4]; + // In case I'm a real-valued event. + double bitsr_[4]; }; /* @@ -235,9 +240,6 @@ class vvp_fun_anyedge_sa : public vvp_fun_anyedge { private: vthread_t threads_; - vvp_vector4_t bits_[4]; - // In case I'm a real-valued event. - double bitsr_[4]; }; /* diff --git a/vvp/part.cc b/vvp/part.cc index 4aa365af8..bd0dedcf1 100644 --- a/vvp/part.cc +++ b/vvp/part.cc @@ -58,10 +58,15 @@ void vvp_fun_part_sa::recv_vec4(vvp_net_ptr_t port, const vvp_vector4_t&bit, { assert(port.port() == 0); - if (val_ .eeq( bit )) + vvp_vector4_t tmp (wid_, BIT4_X); + for (unsigned idx = 0 ; idx < wid_ ; idx += 1) { + if (idx + base_ < bit.size()) + tmp.set_bit(idx, bit.value(base_+idx)); + } + if (val_ .eeq( tmp )) return; - val_ = bit; + val_ = tmp; if (net_ == 0) { net_ = port.ptr(); @@ -94,13 +99,7 @@ void vvp_fun_part_sa::run_run() { vvp_net_t*ptr = net_; net_ = 0; - - vvp_vector4_t res (wid_, BIT4_X); - for (unsigned idx = 0 ; idx < wid_ ; idx += 1) { - if (idx + base_ < val_.size()) - res.set_bit(idx, val_.value(base_+idx)); - } - ptr->send_vec4(res, 0); + ptr->send_vec4(val_, 0); } vvp_fun_part_aa::vvp_fun_part_aa(unsigned base, unsigned wid)