Check what can drive a variable in SystemVerilog

This commit is contained in:
Cary R 2025-06-30 23:47:38 -07:00
parent a05da1ca08
commit 66d57628bf
7 changed files with 91 additions and 39 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 1998-2024 Stephen Williams <steve@icarus.com> * Copyright (c) 1998-2025 Stephen Williams <steve@icarus.com>
* Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
@ -54,7 +54,7 @@ bool PExpr::has_aa_term(Design*, NetScope*) const
return false; return false;
} }
NetNet* PExpr::elaborate_lnet(Design*, NetScope*) const NetNet* PExpr::elaborate_lnet(Design*, NetScope*, bool) const
{ {
cerr << get_fileline() << ": error: " cerr << get_fileline() << ": error: "
<< "expression not valid in assign l-value: " << "expression not valid in assign l-value: "
@ -62,7 +62,7 @@ NetNet* PExpr::elaborate_lnet(Design*, NetScope*) const
return 0; return 0;
} }
NetNet* PExpr::elaborate_bi_net(Design*, NetScope*) const NetNet* PExpr::elaborate_bi_net(Design*, NetScope*, bool) const
{ {
cerr << get_fileline() << ": error: " cerr << get_fileline() << ": error: "
<< "expression not valid as argument to inout port: " << "expression not valid as argument to inout port: "

24
PExpr.h
View File

@ -1,7 +1,7 @@
#ifndef IVL_PExpr_H #ifndef IVL_PExpr_H
#define IVL_PExpr_H #define IVL_PExpr_H
/* /*
* Copyright (c) 1998-2024 Stephen Williams <steve@icarus.com> * Copyright (c) 1998-2025 Stephen Williams <steve@icarus.com>
* Copyright CERN 2013 / Stephen Williams (steve@icarus.com) * Copyright CERN 2013 / Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
@ -156,13 +156,15 @@ class PExpr : public LineInfo {
// This method elaborates the expression as gates, but // This method elaborates the expression as gates, but
// restricted for use as l-values of continuous assignments. // 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 // This is similar to elaborate_lnet, except that the
// expression is evaluated to be bi-directional. This is // expression is evaluated to be bi-directional. This is
// useful for arguments to inout ports of module instances and // useful for arguments to inout ports of module instances and
// ports of tran primitives. // 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 // Expressions that can be in the l-value of procedural
// assignments can be elaborated with this method. If the // 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, virtual unsigned test_width(Design*des, NetScope*scope,
width_mode_t&mode); width_mode_t&mode);
virtual NetNet* elaborate_lnet(Design*des, NetScope*scope) const; virtual NetNet* elaborate_lnet(Design*des, NetScope*scope,
virtual NetNet* elaborate_bi_net(Design*des, NetScope*scope) const; 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, virtual NetExpr*elaborate_expr(Design*des, NetScope*scope,
ivl_type_t type, unsigned flags) const; ivl_type_t type, unsigned flags) const;
@ -269,7 +273,8 @@ class PEConcat : public PExpr {
NetNet::PortType port_type) const; NetNet::PortType port_type) const;
private: private:
NetNet* elaborate_lnet_common_(Design*des, NetScope*scope, NetNet* elaborate_lnet_common_(Design*des, NetScope*scope,
bool bidirectional_flag) const; bool bidirectional_flag,
bool var_allowed_in_sv) const;
private: private:
std::vector<PExpr*>parms_; std::vector<PExpr*>parms_;
std::valarray<width_mode_t>width_modes_; std::valarray<width_mode_t>width_modes_;
@ -355,9 +360,9 @@ class PEIdent : public PExpr {
width_mode_t&mode); width_mode_t&mode);
// Identifiers are allowed (with restrictions) is assign l-values. // 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. // Identifiers are also allowed as procedural assignment l-values.
virtual NetAssign_* elaborate_lval(Design*des, virtual NetAssign_* elaborate_lval(Design*des,
@ -545,7 +550,8 @@ class PEIdent : public PExpr {
private: private:
NetNet* elaborate_lnet_common_(Design*des, NetScope*scope, 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, bool eval_part_select_(Design*des, NetScope*scope, NetNet*sig,

View File

@ -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) * Copyright CERN 2012 / Stephen Williams (steve@icarus.com)
* *
* This source code is free software; you can redistribute it * This source code is free software; you can redistribute it
@ -43,7 +43,8 @@ using namespace std;
* make the l-value connections. * make the l-value connections.
*/ */
NetNet* PEConcat::elaborate_lnet_common_(Design*des, NetScope*scope, 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); ivl_assert(*this, scope);
@ -76,9 +77,9 @@ NetNet* PEConcat::elaborate_lnet_common_(Design*des, NetScope*scope,
} }
if (bidirectional_flag) { 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 { } 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) { if (nets[idx] == 0) {
@ -163,14 +164,16 @@ NetNet* PEConcat::elaborate_lnet_common_(Design*des, NetScope*scope,
return osig; 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, 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. * so most of the work for both is done here.
*/ */
NetNet* PEIdent::elaborate_lnet_common_(Design*des, NetScope*scope, 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); 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 // If this is SystemVerilog and the variable is not yet
// assigned by anything, then convert it to an unresolved // assigned by anything, then convert it to an unresolved
// wire. // wire.
if (gn_var_can_be_uwire() if (gn_var_can_be_uwire() && var_allowed_in_sv
&& (sig->type() == NetNet::REG) && (sig->type() == NetNet::REG)
&& (sig->peek_lref() == 0) ) { && (sig->peek_lref() == 0) ) {
sig->type(NetNet::UNRESOLVED_WIRE); 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. // Don't allow registers as assign l-values.
if (sig->type() == NetNet::REG) { if (sig->type() == NetNet::REG) {
cerr << get_fileline() << ": error: reg " << sig->name() cerr << get_fileline() << ": error: variable " << sig->name()
<< "; cannot be driven by primitives" << "; cannot be driven by a primitive or continuous assignment";
<< " or continuous assignment." << endl; if(gn_var_can_be_uwire()) {
cerr << " with non-default strength";
}
cerr << "." << endl;
des->errors += 1; des->errors += 1;
return 0; return nullptr;
} }
// Some parts below need the tail component. This is a convenient // 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() cerr << get_fileline() << ": error: Array " << path()
<< " needs " << sig->unpacked_dimensions() << " indices," << " needs " << sig->unpacked_dimensions() << " indices,"
<< " but got only " << path_tail.index.size() << ". (net)" << endl; << " but got only " << path_tail.index.size() << ". (net)" << endl;
cerr << get_fileline() << ": : Assignment to a whole array requires SystemVerilog."
<< endl;
des->errors += 1; des->errors += 1;
return 0; 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 * Identifiers in continuous assignment l-values are limited to wires
* and that ilk. Detect registers and memories here and report errors. * 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);
} }
/* /*

View File

@ -124,7 +124,10 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const
ivl_assert(*this, pin(1)); ivl_assert(*this, pin(1));
/* Elaborate the l-value. */ /* 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) { if (lval == 0) {
return; return;
} }
@ -771,10 +774,11 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const
des->errors += 1; des->errors += 1;
return; return;
} }
// Gates can never have variable output ports.
if (lval_count > gate_count) 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 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 // The only way this should return zero is if an error
// happened, so for that case just return. // 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 Use the elaborate_bi_net method to handle all
the possible cases. */ 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) { if (sig == 0) {
cerr << pins[idx]->get_fileline() << ": error: " cerr << pins[idx]->get_fileline() << ": error: "
<< "Inout port expression must support " << "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 assignment, as the port will continuous assign
into the port. */ 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) { if (sig == 0) {
cerr << pins[idx]->get_fileline() << ": error: " cerr << pins[idx]->get_fileline() << ": error: "
<< "Output port expression must support " << "Output port expression must support "
@ -2196,7 +2202,8 @@ void PGModule::elaborate_udp_(Design*des, PUdp*udp, NetScope*scope) const
<< endl; << endl;
} else { } 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) { if (sig == 0) {
cerr << get_fileline() << ": error: " cerr << get_fileline() << ": error: "
<< "Output port expression is not valid." << endl; << "Output port expression is not valid." << endl;

View File

@ -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

View File

@ -228,6 +228,10 @@ br_gh782b normal,-g2009 ivltests gold=br_gh782b.gold
br_gh800 normal,-g2009 ivltests br_gh800 normal,-g2009 ivltests
br_gh801 normal,-g2012 ivltests br_gh801 normal,-g2012 ivltests
br_gh801b 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_ml20171017 normal,-g2009 ivltests
br_ml20180227 CE,-g2009 ivltests br_ml20180227 CE,-g2009 ivltests
br_ml20180309a normal,-g2009 ivltests br_ml20180309a normal,-g2009 ivltests

View File

@ -364,9 +364,6 @@ br_gh1178a CE ivltests gold=br_gh1178a.gold
br_gh1178b normal ivltests br_gh1178b normal ivltests
br_gh1178c normal ivltests br_gh1178c normal ivltests
br_gh1182 CE ivltests gold=br_gh1182.gold 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_gh1225a CE ivltests gold=br_gh1225a.gold
br_gh1225b CE ivltests gold=br_gh1225b.gold br_gh1225b CE ivltests gold=br_gh1225b.gold
br_gh1225c CE ivltests gold=br_gh1225c.gold br_gh1225c CE ivltests gold=br_gh1225c.gold