diff --git a/PExpr.cc b/PExpr.cc index 35f0ed2ef..da7baa947 100644 --- a/PExpr.cc +++ b/PExpr.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998-2024 Stephen Williams + * Copyright (c) 1998-2025 Stephen Williams * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -54,7 +54,7 @@ bool PExpr::has_aa_term(Design*, NetScope*) const return false; } -NetNet* PExpr::elaborate_lnet(Design*, NetScope*) const +NetNet* PExpr::elaborate_lnet(Design*, NetScope*, bool) const { cerr << get_fileline() << ": error: " << "expression not valid in assign l-value: " @@ -62,7 +62,7 @@ NetNet* PExpr::elaborate_lnet(Design*, NetScope*) const return 0; } -NetNet* PExpr::elaborate_bi_net(Design*, NetScope*) const +NetNet* PExpr::elaborate_bi_net(Design*, NetScope*, bool) const { cerr << get_fileline() << ": error: " << "expression not valid as argument to inout port: " diff --git a/PExpr.h b/PExpr.h index ba494ada3..116490c50 100644 --- a/PExpr.h +++ b/PExpr.h @@ -1,7 +1,7 @@ #ifndef IVL_PExpr_H #define IVL_PExpr_H /* - * Copyright (c) 1998-2024 Stephen Williams + * Copyright (c) 1998-2025 Stephen Williams * Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -156,13 +156,15 @@ class PExpr : public LineInfo { // This method elaborates the expression as gates, but // restricted for use as l-values of continuous assignments. - virtual NetNet* elaborate_lnet(Design*des, NetScope*scope) const; + virtual NetNet* elaborate_lnet(Design*des, NetScope*scope, + bool var_allowed_in_sv) const; // This is similar to elaborate_lnet, except that the // expression is evaluated to be bi-directional. This is // useful for arguments to inout ports of module instances and // ports of tran primitives. - virtual NetNet* elaborate_bi_net(Design*des, NetScope*scope) const; + virtual NetNet* elaborate_bi_net(Design*des, NetScope*scope, + bool var_allowed_in_sv) const; // Expressions that can be in the l-value of procedural // assignments can be elaborated with this method. If the @@ -251,8 +253,10 @@ class PEConcat : public PExpr { virtual unsigned test_width(Design*des, NetScope*scope, width_mode_t&mode); - virtual NetNet* elaborate_lnet(Design*des, NetScope*scope) const; - virtual NetNet* elaborate_bi_net(Design*des, NetScope*scope) const; + virtual NetNet* elaborate_lnet(Design*des, NetScope*scope, + bool var_allowed_in_sv) const; + virtual NetNet* elaborate_bi_net(Design*des, NetScope*scope, + bool var_allowed_in_sv) const; virtual NetExpr*elaborate_expr(Design*des, NetScope*scope, ivl_type_t type, unsigned flags) const; @@ -269,7 +273,8 @@ class PEConcat : public PExpr { NetNet::PortType port_type) const; private: NetNet* elaborate_lnet_common_(Design*des, NetScope*scope, - bool bidirectional_flag) const; + bool bidirectional_flag, + bool var_allowed_in_sv) const; private: std::vectorparms_; std::valarraywidth_modes_; @@ -355,9 +360,9 @@ class PEIdent : public PExpr { width_mode_t&mode); // Identifiers are allowed (with restrictions) is assign l-values. - virtual NetNet* elaborate_lnet(Design*des, NetScope*scope) const; + virtual NetNet* elaborate_lnet(Design*des, NetScope*scope, bool var_allowed_in_sv) const; - virtual NetNet* elaborate_bi_net(Design*des, NetScope*scope) const; + virtual NetNet* elaborate_bi_net(Design*des, NetScope*scope, bool var_allowed_in_sv) const; // Identifiers are also allowed as procedural assignment l-values. virtual NetAssign_* elaborate_lval(Design*des, @@ -545,7 +550,8 @@ class PEIdent : public PExpr { private: NetNet* elaborate_lnet_common_(Design*des, NetScope*scope, - bool bidirectional_flag) const; + bool bidirectional_flag, + bool var_allowed_in_sv) const; bool eval_part_select_(Design*des, NetScope*scope, NetNet*sig, diff --git a/elab_net.cc b/elab_net.cc index 8fdadcc79..077bc1189 100644 --- a/elab_net.cc +++ b/elab_net.cc @@ -1,5 +1,5 @@ /* - * Copyright (c) 1999-2024 Stephen Williams (steve@icarus.com) + * Copyright (c) 1999-2025 Stephen Williams (steve@icarus.com) * Copyright CERN 2012 / Stephen Williams (steve@icarus.com) * * This source code is free software; you can redistribute it @@ -43,7 +43,8 @@ using namespace std; * make the l-value connections. */ NetNet* PEConcat::elaborate_lnet_common_(Design*des, NetScope*scope, - bool bidirectional_flag) const + bool bidirectional_flag, + bool var_allowed_in_sv) const { ivl_assert(*this, scope); @@ -76,9 +77,9 @@ NetNet* PEConcat::elaborate_lnet_common_(Design*des, NetScope*scope, } if (bidirectional_flag) { - nets[idx] = parms_[idx]->elaborate_bi_net(des, scope); + nets[idx] = parms_[idx]->elaborate_bi_net(des, scope, var_allowed_in_sv); } else { - nets[idx] = parms_[idx]->elaborate_lnet(des, scope); + nets[idx] = parms_[idx]->elaborate_lnet(des, scope, var_allowed_in_sv); } if (nets[idx] == 0) { @@ -163,14 +164,16 @@ NetNet* PEConcat::elaborate_lnet_common_(Design*des, NetScope*scope, return osig; } -NetNet* PEConcat::elaborate_lnet(Design*des, NetScope*scope) const +NetNet* PEConcat::elaborate_lnet(Design*des, NetScope*scope, + bool var_allowed_in_sv) const { - return elaborate_lnet_common_(des, scope, false); + return elaborate_lnet_common_(des, scope, false, var_allowed_in_sv); } -NetNet* PEConcat::elaborate_bi_net(Design*des, NetScope*scope) const +NetNet* PEConcat::elaborate_bi_net(Design*des, NetScope*scope, + bool var_allowed_in_sv) const { - return elaborate_lnet_common_(des, scope, true); + return elaborate_lnet_common_(des, scope, true, var_allowed_in_sv); } bool PEConcat::is_collapsible_net(Design*des, NetScope*scope, @@ -518,7 +521,8 @@ bool PEIdent::eval_part_select_(Design*des, NetScope*scope, NetNet*sig, * so most of the work for both is done here. */ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, - bool bidirectional_flag) const + bool bidirectional_flag, + bool var_allowed_in_sv) const { ivl_assert(*this, scope); @@ -569,7 +573,7 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, // If this is SystemVerilog and the variable is not yet // assigned by anything, then convert it to an unresolved // wire. - if (gn_var_can_be_uwire() + if (gn_var_can_be_uwire() && var_allowed_in_sv && (sig->type() == NetNet::REG) && (sig->peek_lref() == 0) ) { sig->type(NetNet::UNRESOLVED_WIRE); @@ -577,11 +581,14 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, // Don't allow registers as assign l-values. if (sig->type() == NetNet::REG) { - cerr << get_fileline() << ": error: reg " << sig->name() - << "; cannot be driven by primitives" - << " or continuous assignment." << endl; + cerr << get_fileline() << ": error: variable " << sig->name() + << "; cannot be driven by a primitive or continuous assignment"; + if(gn_var_can_be_uwire()) { + cerr << " with non-default strength"; + } + cerr << "." << endl; des->errors += 1; - return 0; + return nullptr; } // Some parts below need the tail component. This is a convenient @@ -758,6 +765,8 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, cerr << get_fileline() << ": error: Array " << path() << " needs " << sig->unpacked_dimensions() << " indices," << " but got only " << path_tail.index.size() << ". (net)" << endl; + cerr << get_fileline() << ": : Assignment to a whole array requires SystemVerilog." + << endl; des->errors += 1; return 0; } @@ -982,14 +991,16 @@ NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, * Identifiers in continuous assignment l-values are limited to wires * and that ilk. Detect registers and memories here and report errors. */ -NetNet* PEIdent::elaborate_lnet(Design*des, NetScope*scope) const +NetNet* PEIdent::elaborate_lnet(Design*des, NetScope*scope, + bool var_allowed_in_sv) const { - return elaborate_lnet_common_(des, scope, false); + return elaborate_lnet_common_(des, scope, false, var_allowed_in_sv); } -NetNet* PEIdent::elaborate_bi_net(Design*des, NetScope*scope) const +NetNet* PEIdent::elaborate_bi_net(Design*des, NetScope*scope, + bool var_allowed_in_sv) const { - return elaborate_lnet_common_(des, scope, true); + return elaborate_lnet_common_(des, scope, true, var_allowed_in_sv); } /* diff --git a/elaborate.cc b/elaborate.cc index ed682b22d..9cc457af8 100644 --- a/elaborate.cc +++ b/elaborate.cc @@ -124,7 +124,10 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const ivl_assert(*this, pin(1)); /* Elaborate the l-value. */ - NetNet*lval = pin(0)->elaborate_lnet(des, scope); + // A continuous assignment can drive a variable if the default strength is used. + bool var_allowed_in_sv = (drive0 == IVL_DR_STRONG && + drive1 == IVL_DR_STRONG) ? true : false; + NetNet*lval = pin(0)->elaborate_lnet(des, scope, var_allowed_in_sv); if (lval == 0) { return; } @@ -771,10 +774,11 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const des->errors += 1; return; } + // Gates can never have variable output ports. if (lval_count > gate_count) - lval_sigs[idx] = pin(idx)->elaborate_bi_net(des, scope); + lval_sigs[idx] = pin(idx)->elaborate_bi_net(des, scope, false); else - lval_sigs[idx] = pin(idx)->elaborate_lnet(des, scope); + lval_sigs[idx] = pin(idx)->elaborate_lnet(des, scope, false); // The only way this should return zero is if an error // happened, so for that case just return. @@ -1677,7 +1681,8 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const Use the elaborate_bi_net method to handle all the possible cases. */ - sig = pins[idx]->elaborate_bi_net(des, scope); + // A module inout port cannot drive a variable. + sig = pins[idx]->elaborate_bi_net(des, scope, false); if (sig == 0) { cerr << pins[idx]->get_fileline() << ": error: " << "Inout port expression must support " @@ -1739,7 +1744,8 @@ void PGModule::elaborate_mod_(Design*des, Module*rmod, NetScope*scope) const assignment, as the port will continuous assign into the port. */ - sig = pins[idx]->elaborate_lnet(des, scope); + // A module output port can drive a variable. + sig = pins[idx]->elaborate_lnet(des, scope, true); if (sig == 0) { cerr << pins[idx]->get_fileline() << ": error: " << "Output port expression must support " @@ -2196,7 +2202,8 @@ void PGModule::elaborate_udp_(Design*des, PUdp*udp, NetScope*scope) const << endl; } else { - NetNet*sig = pins[0]->elaborate_lnet(des, scope); + // A UDP can drive a variable. + NetNet*sig = pins[0]->elaborate_lnet(des, scope, true); if (sig == 0) { cerr << get_fileline() << ": error: " << "Output port expression is not valid." << endl; diff --git a/ivtest/ivltests/br_gh1222.v b/ivtest/ivltests/br_gh1222.v new file mode 100644 index 000000000..b5e1d0e9c --- /dev/null +++ b/ivtest/ivltests/br_gh1222.v @@ -0,0 +1,27 @@ +module top; + real rout_ca1, rout_ca2, rout_valid, rout_gt, rout_udp; + logic lout_ca1, lout_ca2, lout_valid1, lout_valid2, lout_gt, lout_udp; + reg in; + + assign (weak1, weak0) {rout_ca1, rout_ca2} = in; // Non-default strength so invalid + assign (weak1, weak0) {lout_ca1, lout_ca2} = in; // Non-default strength so invalid + + assign (strong1, strong0) rout_valid = in; // Ok, real cannot be in a concatenation + assign (strong1, strong0) {lout_valid1, lout_valid2} = in; // Ok, default strength + + and (rout_gt, in, in); // Gates must drive a net + and (lout_gt, in, in); // Gates must drive a net + + // When strength is added it should only be for the default strength! + udp_inv (rout_udp, in); // A UDP is like a module and can drive a variable + udp_inv (lout_udp, in); // A UDP is like a module and can drive a variable + + initial $display("FAILED: There should be compile errors!"); +endmodule + +primitive udp_inv (output y, input a); + table + 0 : 1; + 1 : 0; + endtable +endprimitive diff --git a/ivtest/regress-sv.list b/ivtest/regress-sv.list index 6bb2525e3..f2b645b98 100644 --- a/ivtest/regress-sv.list +++ b/ivtest/regress-sv.list @@ -228,6 +228,10 @@ br_gh782b normal,-g2009 ivltests gold=br_gh782b.gold br_gh800 normal,-g2009 ivltests br_gh801 normal,-g2012 ivltests br_gh801b normal,-g2012 ivltests +br_gh1222 CE,-g2009 ivltests gold=br_gh1222.gold +br_gh1223a normal,-g2009 ivltests +br_gh1223b normal,-g2009 ivltests +br_gh1223c normal,-g2009 ivltests br_ml20171017 normal,-g2009 ivltests br_ml20180227 CE,-g2009 ivltests br_ml20180309a normal,-g2009 ivltests diff --git a/ivtest/regress-vlg.list b/ivtest/regress-vlg.list index 5a9c6aa33..0d75499bc 100644 --- a/ivtest/regress-vlg.list +++ b/ivtest/regress-vlg.list @@ -364,9 +364,6 @@ br_gh1178a CE ivltests gold=br_gh1178a.gold br_gh1178b normal ivltests br_gh1178c normal ivltests br_gh1182 CE ivltests gold=br_gh1182.gold -br_gh1223a normal,-g2009 ivltests -br_gh1223b normal,-g2009 ivltests -br_gh1223c normal,-g2009 ivltests br_gh1225a CE ivltests gold=br_gh1225a.gold br_gh1225b CE ivltests gold=br_gh1225b.gold br_gh1225c CE ivltests gold=br_gh1225c.gold