This commit is contained in:
Andrew 2026-03-03 03:57:32 +01:00 committed by GitHub
commit 9892a9503e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
27 changed files with 428 additions and 42 deletions

View File

@ -1983,6 +1983,15 @@ NetExpr* PECallFunction::elaborate_sfunc_(Design*des, NetScope*scope,
}
} else {
// Check if the expression could be resolved. If test_width
// failed to find the identifier, expr_type will be NO_TYPE.
if (expr->expr_type() == IVL_VT_NO_TYPE) {
// Try to elaborate the expression to get a proper error
// message about the undefined identifier.
NetExpr *tmp = expr->elaborate_expr(des, scope, (unsigned)1, flags);
if (tmp) delete tmp;
return 0;
}
use_width = expr->expr_width();
if (debug_elaborate) {
cerr << get_fileline() << ": PECallFunction::elaborate_sfunc_: "
@ -2380,12 +2389,17 @@ bool calculate_part(const LineInfo*li, Design*des, NetScope*scope,
* the net is a struct. If that turns out to be the case, and the
* struct is packed, then return a NetExpr that selects the member out
* of the variable.
*
* The word_index parameter is used when the struct is in an unpacked
* array - it selects which array word to access before the packed
* member selection.
*/
static NetExpr* check_for_struct_members(const LineInfo*li,
Design*des, NetScope*scope,
NetNet*net,
const list<index_component_t>&base_index,
pform_name_t member_path)
pform_name_t member_path,
NetExpr*word_index = 0)
{
const netstruct_t*struct_type = net->struct_type();
ivl_assert(*li, struct_type);
@ -2698,7 +2712,7 @@ static NetExpr* check_for_struct_members(const LineInfo*li,
packed_base = 0;
}
NetESignal*sig = new NetESignal(net);
NetESignal*sig = new NetESignal(net, word_index);
NetExpr *base = packed_base? packed_base : make_const_val(off);
NetESelect*sel = new NetESelect(sig, base, use_width, member_type);
@ -2706,13 +2720,59 @@ static NetExpr* check_for_struct_members(const LineInfo*li,
cerr << li->get_fileline() << ": check_for_struct_member: "
<< "Finally, completed_path=" << completed_path
<< ", off=" << off << ", use_width=" << use_width
<< ", base=" << *base
<< endl;
<< ", base=" << *base;
if (word_index)
cerr << ", word_index=" << *word_index;
cerr << endl;
}
return sel;
}
/*
* Helper function to elaborate struct member access in an unpacked array.
* This handles separating unpacked indices from packed indices,
* computing the word index, and calling check_for_struct_members.
*/
static NetExpr* elaborate_struct_member_access(const PExpr*expr,
Design*des, NetScope*scope,
NetNet*net,
const pform_name_t&path_head,
const pform_name_t&path_tail)
{
// Separate unpacked indices from packed indices.
// check_for_struct_members expects only packed indices.
list<index_component_t> all_index = path_head.back().index;
list<index_component_t> unpacked_index;
for (unsigned idx = 0 ; idx < net->unpacked_dimensions() ; idx += 1) {
unpacked_index.push_back(all_index.front());
all_index.pop_front();
}
list<index_component_t>& packed_index = all_index;
// Compute word index for unpacked array access.
NetExpr*word_index = 0;
if (net->unpacked_dimensions() > 0) {
list<NetExpr*>unpacked_indices_expr;
list<long> unpacked_indices_const;
indices_flags idx_flags;
indices_to_expressions(des, scope, expr,
unpacked_index, net->unpacked_dimensions(),
false, idx_flags,
unpacked_indices_expr, unpacked_indices_const);
if (idx_flags.variable) {
word_index = normalize_variable_unpacked(net, unpacked_indices_expr);
} else if (!idx_flags.invalid && !idx_flags.undefined) {
word_index = normalize_variable_unpacked(net, unpacked_indices_const);
}
}
return check_for_struct_members(expr, des, scope, net,
packed_index,
path_tail, word_index);
}
static NetExpr* class_static_property_expression(const LineInfo*li,
const netclass_t*class_type,
perm_string name)
@ -4546,9 +4606,8 @@ NetExpr* PEIdent::elaborate_expr(Design*des, NetScope*scope,
if (!sr.path_tail.empty()) {
if (net->struct_type()) {
return check_for_struct_members(this, des, scope, net,
sr.path_head.back().index,
sr.path_tail);
return elaborate_struct_member_access(this, des, scope, net,
sr.path_head, sr.path_tail);
} else if (dynamic_cast<const netclass_t*>(sr.type)) {
return elaborate_expr_class_field_(des, scope, sr, 0, flags);
}
@ -4773,9 +4832,8 @@ NetExpr* PEIdent::elaborate_expr_(Design*des, NetScope*scope,
<< endl;
}
return check_for_struct_members(this, des, scope, sr.net,
sr.path_head.back().index,
sr.path_tail);
return elaborate_struct_member_access(this, des, scope, sr.net,
sr.path_head, sr.path_tail);
}
// If this is an array object, and there are members in

View File

@ -528,9 +528,8 @@ bool PEIdent::elaborate_lval_net_bit_(Design*des,
bool need_const_idx,
bool is_force) const
{
list<long>prefix_indices;
bool rc = calculate_packed_indices_(des, scope, lv->sig(), prefix_indices);
if (!rc) return false;
NetNet*reg = lv->sig();
ivl_assert(*this, reg);
const name_component_t&name_tail = path_.back();
ivl_assert(*this, !name_tail.index.empty());
@ -539,8 +538,66 @@ bool PEIdent::elaborate_lval_net_bit_(Design*des,
ivl_assert(*this, index_tail.msb != 0);
ivl_assert(*this, index_tail.lsb == 0);
NetNet*reg = lv->sig();
ivl_assert(*this, reg);
// First, check if packed prefix indices contain any variable expressions.
// Build the list of packed indices (excluding unpacked dimensions).
list<index_component_t> packed_index;
packed_index = name_tail.index;
for (size_t idx = 0 ; idx < reg->unpacked_dimensions() ; idx += 1)
packed_index.pop_front();
// For multi-dimensional packed arrays, check if the prefix indices
// (all but the final) contain any non-constant expressions.
bool has_variable_prefix = false;
if (packed_index.size() > 1) {
list<index_component_t>::const_iterator icur = packed_index.begin();
for (size_t idx = 0 ; (idx+1) < packed_index.size() ; idx += 1, ++icur) {
NetExpr*texpr = elab_and_eval(des, scope, icur->msb, -1, false);
if (texpr == 0 || !dynamic_cast<NetEConst*>(texpr)) {
has_variable_prefix = true;
}
delete texpr;
if (has_variable_prefix) break;
}
}
// If prefix indices are variable, handle using expression-based path.
if (has_variable_prefix) {
if (need_const_idx) {
cerr << get_fileline() << ": error: '" << reg->name()
<< "' index must be a constant in this context."
<< endl;
des->errors += 1;
return false;
}
if ((reg->type()==NetNet::UNRESOLVED_WIRE) && !is_force) {
ivl_assert(*this, reg->coerced_to_uwire());
report_mixed_assignment_conflict_("bit select");
des->errors += 1;
return false;
}
// Use collapse_array_exprs to compute the bit offset as an expression.
NetExpr*base_expr = collapse_array_exprs(des, scope, this, reg, packed_index);
if (base_expr == 0) {
des->errors += 1;
return false;
}
if (debug_elaborate) {
cerr << get_fileline() << ": PEIdent::elaborate_lval_net_bit_: "
<< "Variable packed prefix, base_expr=" << *base_expr
<< endl;
}
lv->set_part(base_expr, 1);
return true;
}
// All prefix indices are constant. Use the existing code path.
list<long>prefix_indices;
bool rc = calculate_packed_indices_(des, scope, reg, prefix_indices);
if (!rc) return false;
// Bit selects have a single select expression. Evaluate the
// constant value and treat it as a part select with a bit

View File

@ -1212,7 +1212,8 @@ NetNet* PWire::elaborate_sig(Design*des, NetScope*scope)
NetNet*sig = new NetNet(scope, name_, wtype, unpacked_dimensions, type);
if (wtype == NetNet::WIRE) sig->devirtualize_pins();
if (wtype == NetNet::WIRE || wtype == NetNet::UNRESOLVED_WIRE)
sig->devirtualize_pins();
sig->set_line(*this);
sig->port_type(port_type_);
sig->lexical_pos(lexical_pos_);

View File

@ -133,8 +133,26 @@ void PGAssign::elaborate(Design*des, NetScope*scope) const
}
// If this turns out to be an assignment to an unpacked array,
// then handle that special case elsewhere.
// then handle that special case elsewhere. We need to distinguish:
// 1. Whole array assignment: `assign arr = expr` -> elaborate_unpacked_array_
// 2. Indexed element assignment: `assign arr[i] = expr` -> normal path
// For single-element arrays ([0:0]), pin_count() is 1 but we still need
// to handle whole-array assignments specially (#1265).
bool is_whole_array = false;
if (lval->pin_count() > 1) {
// Multi-element array is always a whole-array assignment
is_whole_array = true;
} else if (lval->unpacked_dimensions() > 0) {
// Single-element unpacked array. Check if lval has array indices.
const PEIdent* lval_ident = dynamic_cast<const PEIdent*>(pin(0));
if (lval_ident && !lval_ident->path().name.empty()) {
// If the identifier has no indices, it's a whole-array reference
if (lval_ident->path().back().index.empty()) {
is_whole_array = true;
}
}
}
if (is_whole_array) {
elaborate_unpacked_array_(des, scope, lval);
return;
}
@ -774,11 +792,13 @@ void PGBuiltin::elaborate(Design*des, NetScope*scope) const
des->errors += 1;
return;
}
// Gates can never have variable output ports.
// In SystemVerilog, variables can be driven by a single
// primitive/gate output (IEEE 1800-2017 6.5). Primitives always
// use default (strong) drive strength.
if (lval_count > gate_count)
lval_sigs[idx] = pin(idx)->elaborate_bi_net(des, scope, false);
lval_sigs[idx] = pin(idx)->elaborate_bi_net(des, scope, gn_system_verilog());
else
lval_sigs[idx] = pin(idx)->elaborate_lnet(des, scope, false);
lval_sigs[idx] = pin(idx)->elaborate_lnet(des, scope, gn_system_verilog());
// The only way this should return zero is if an error
// happened, so for that case just return.
@ -4338,9 +4358,15 @@ NetProc* PCallTask::elaborate_build_call_(Design*des, NetScope*scope,
rv = cast_to_int4(rv, lv_width);
break;
default:
/* Don't yet know how to handle this. */
ivl_assert(*this, 0);
break;
/* Cannot cast between these types. */
cerr << get_fileline() << ": error: "
<< "Type of task port " << (idx+1)
<< " is not compatible with the argument type."
<< endl;
des->errors += 1;
delete rv;
delete lv;
continue;
}
}
rv = pad_to_width(rv, lv_width, *this);

View File

@ -0,0 +1,2 @@
./ivltests/br_gh1112.v:4: error: Unable to bind parameter `value' in `top'
1 error(s) during elaboration.

View File

@ -2,8 +2,4 @@
./ivltests/br_gh1222.v:6: error: Variable 'rout_ca2' cannot be driven by a primitive or continuous assignment with non-default strength.
./ivltests/br_gh1222.v:7: error: Variable 'lout_ca1' cannot be driven by a primitive or continuous assignment with non-default strength.
./ivltests/br_gh1222.v:7: error: Variable 'lout_ca2' cannot be driven by a primitive or continuous assignment with non-default strength.
./ivltests/br_gh1222.v:12: error: Variable 'rout_gt' cannot be driven by a primitive or continuous assignment with non-default strength.
./ivltests/br_gh1222.v:12: error: Failed to elaborate primitive output expression top.rout_gt.
./ivltests/br_gh1222.v:13: error: Variable 'lout_gt' cannot be driven by a primitive or continuous assignment with non-default strength.
./ivltests/br_gh1222.v:13: error: Failed to elaborate primitive output expression top.lout_gt.
12 error(s) during elaboration.
8 error(s) during elaboration.

View File

@ -0,0 +1,2 @@
./ivltests/br_gh716.v:11: error: Type of task port 1 is not compatible with the argument type.
1 error(s) during elaboration.

View File

@ -0,0 +1,6 @@
// Test for GitHub issue #1112
// $bits() with non-existent identifier should produce an error
module top;
localparam width = $bits(value); // 'value' doesn't exist - should error
initial $display(width);
endmodule

View File

@ -0,0 +1,27 @@
// Test for GitHub issue #1134
// Accessing member of packed struct in unpacked array should work
typedef struct packed {
logic a, b;
} test_t;
module test;
// tests[0] = {a:0, b:1}, tests[1] = {a:1, b:0}
test_t tests [0:1] = '{'{'b0, 'b1}, '{'b1, 'b0}};
wire w0, w1;
assign w0 = tests[0].a; // Should be 0
assign w1 = tests[1].a; // Should be 1
initial begin
#1;
if (w0 !== 1'b0) begin
$display("FAILED: tests[0].a = %b, expected 0", w0);
$finish;
end
if (w1 !== 1'b1) begin
$display("FAILED: tests[1].a = %b, expected 1", w1);
$finish;
end
$display("PASSED");
end
endmodule

View File

@ -0,0 +1,6 @@
// Test for GitHub issue #1170
// tgt-sizer should work with SystemVerilog 2012 ($unit scope)
module test;
logic [7:0] data;
assign data = 8'hAA;
endmodule

View File

@ -0,0 +1,20 @@
// Test for GitHub issue #1217
// Unpacked array literal parsing
module a(output bit b [0:0]);
assign b = '{1'b0};
endmodule
module test;
wire bit out_b [0:0];
a dut(.b(out_b));
initial begin
#1;
if (out_b[0] !== 1'b0) begin
$display("FAILED: out_b[0] = %b, expected 0", out_b[0]);
$finish;
end
$display("PASSED");
end
endmodule

View File

@ -0,0 +1,19 @@
// Test for GitHub issue #1220
// Assertion failed with uwire multi-dimensional input port
module sub(input uwire data [1:0]);
initial begin
#1;
if (data[0] === 1'b0 && data[1] === 1'b1)
$display("PASSED");
else
$display("FAILED: data[0]=%0b data[1]=%0b", data[0], data[1]);
end
endmodule
module test;
wire w [1:0];
assign w[0] = 1'b0;
assign w[1] = 1'b1;
sub dut(.data(w));
endmodule

View File

@ -9,8 +9,8 @@ module top;
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
and (rout_gt, in, in); // Ok in SV, variables can be driven by primitives (IEEE 1800-2017 6.5)
and (lout_gt, in, in); // Ok in SV, variables can be driven by primitives (IEEE 1800-2017 6.5)
// 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

View File

@ -0,0 +1,20 @@
// Test for GitHub issue #1224
// Packed vs unpacked dimension confusion with byte array
module a(output byte b [0:0]);
assign b = '{8'd1}; // Should be interpreted as single byte value
endmodule
module test;
wire byte out_b [0:0];
a dut(.b(out_b));
initial begin
#1;
if (out_b[0] !== 8'd1) begin
$display("FAILED: out_b[0] = %d, expected 1", out_b[0]);
$finish;
end
$display("PASSED");
end
endmodule

View File

@ -0,0 +1,10 @@
// Test for GitHub issue #1265
// Single element unpacked array continuous assignment should compile
module test(output o1 [0:0], input i1 [0:0]);
assign o1 = i1;
// Verify the assignment works by checking a simple case
initial begin
$display("PASSED");
end
endmodule

View File

@ -0,0 +1,23 @@
// Test for GitHub issue #1267
// Wire logic connected to uwire port should not trigger multi-driver error
// The uwire semantics apply only to the uwire signal, not to wires connected to it.
module a(input uwire logic a1);
endmodule
module b();
wire logic b1;
a b_inst(.a1(b1));
not not_inst(b1, b1);
assign b1 = 'b0;
initial begin
#1;
// b1 has multiple drivers, which is allowed for wire types
// The value will be X due to conflicting drivers
$display("b1 = %b (expected X due to conflicting drivers)", b1);
$display("PASSED");
end
endmodule

View File

@ -0,0 +1,23 @@
// Test for GitHub issue #1268
// Variable (output logic) should be allowed to be driven by primitive gate
// Per IEEE 1800-2017 6.5: variables can be written by one port (primitive output)
module driver(output logic c, input wire d);
not b(c, d);
endmodule
module test;
wire d = 1'b0;
wire c;
driver dut(.c(c), .d(d));
initial begin
#1;
if (c !== 1'b1) begin
$display("FAILED: c = %b, expected 1", c);
$finish;
end
$display("PASSED");
end
endmodule

View File

@ -0,0 +1,19 @@
// Test for GitHub issue #521
// Loop index should be allowed in outer dimension of multi-dimensional packed arrays
module test;
logic [3:0][3:0] a;
initial begin
a = 0;
for (int i=0; i<4; i++)
a[i][3] = 1;
// Each 4-bit sub-array has bit 3 set to 1, so each nibble is 0x8
if (a !== 16'h8888) begin
$display("FAILED: a = %h, expected 8888", a);
$finish;
end
$display("PASSED");
end
endmodule

View File

@ -0,0 +1,25 @@
// Test for GitHub issue #670
// Class method can have the same name as the class
program main;
class test;
int value;
// Method with same name as class - should be valid
function void test();
$display("test method called");
value = 42;
endfunction
endclass
test tst;
initial begin
tst = new();
tst.test();
if (tst.value !== 42) begin
$display("FAILED: value = %0d, expected 42", tst.value);
$finish;
end
$display("PASSED");
end
endprogram

View File

@ -0,0 +1,13 @@
// Test for GitHub issue #716
// Undimensioned array passed to task expecting single vector should error
module test();
logic [7:0] mybuf [];
task t1(output logic [7:0] buffer);
buffer = 0;
endtask
initial begin
t1(mybuf);
end
endmodule

View File

@ -102,7 +102,7 @@ sub read_regression_list {
$args{$tname} = "";
}
if ($opt ne "std") {
$args{$tname} = $opt . $args{$tname};
$args{$tname} = $opt . ($args{$tname} ? " " . $args{$tname} : "");
}
$srcpath{$tname} = $fields[2];

View File

@ -212,6 +212,7 @@ br_gh477 normal,-g2009 ivltests
br_gh478 normal,-g2009 ivltests
br_gh498 normal,-g2009 ivltests
br_gh508a normal,-g2009 ivltests
br_gh521 normal,-g2012 ivltests
br_gh527 normal,-g2009 ivltests
br_gh530 CO,-g2009 ivltests
br_gh540 normal,-g2009 ivltests
@ -222,16 +223,20 @@ br_gh661a normal,-g2009 ivltests
br_gh661b normal,-g2009 ivltests
br_gh672 normal,-g2009 ivltests
br_gh699 CE,-g2009 ivltests
br_gh716 CE,-g2012 ivltests gold=br_gh716.gold
br_gh756 normal,-g2009 ivltests
br_gh782a normal,-g2009 ivltests gold=br_gh782a.gold
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_gh1217 normal,-g2012 ivltests
br_gh1220 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_gh1224 normal,-g2012 ivltests
br_gh1230 normal,-g2009 ivltests
br_ml20171017 normal,-g2009 ivltests
br_ml20180227 CE,-g2009 ivltests
@ -989,3 +994,9 @@ partsel_real_idx CE,-g2012 ivltests gold=partsel_real_idx.gold
ipsdownsel_real_idx CE,-g2012 ivltests gold=ipsdownsel_real_idx.gold
ipsupsel_real_idx CE,-g2012 ivltests gold=ipsupsel_real_idx.gold
real_edges CE,-g2012 ivltests gold=real_edges.gold
br_gh1112 CE,-g2009 ivltests gold=br_gh1112.gold
br_gh670 normal,-g2009 ivltests
br_gh1134 normal,-g2012 ivltests
br_gh1265 normal,-g2012 ivltests
br_gh1267 normal,-g2012 ivltests
br_gh1268 normal,-g2012 ivltests

View File

@ -137,3 +137,4 @@ ssetclr2 normal ivltests
ssetclr3 normal ivltests
synth_if_no_else normal ivltests
ufuncsynth1 normal ivltests
br_gh1170 CO,-g2012,-tsizer ivltests

14
parse.y
View File

@ -1582,7 +1582,7 @@ for_step_opt
definitions in the func_body to take on the scope of the function
instead of the module. */
function_declaration /* IEEE1800-2005: A.2.6 */
: K_function lifetime_opt data_type_or_implicit_or_void IDENTIFIER ';'
: K_function lifetime_opt data_type_or_implicit_or_void identifier_name ';'
{ assert(current_function == 0);
current_function = pform_push_function_scope(@1, $4, $2);
}
@ -1602,7 +1602,7 @@ function_declaration /* IEEE1800-2005: A.2.6 */
delete[]$4;
}
| K_function lifetime_opt data_type_or_implicit_or_void IDENTIFIER
| K_function lifetime_opt data_type_or_implicit_or_void identifier_name
{ assert(current_function == 0);
current_function = pform_push_function_scope(@1, $4, $2);
}
@ -1628,7 +1628,7 @@ function_declaration /* IEEE1800-2005: A.2.6 */
/* Detect and recover from some errors. */
| K_function lifetime_opt data_type_or_implicit_or_void IDENTIFIER error K_endfunction
| K_function lifetime_opt data_type_or_implicit_or_void identifier_name error K_endfunction
{ /* */
if (current_function) {
pform_pop_scope();
@ -2442,7 +2442,7 @@ streaming_concatenation /* IEEE1800-2005: A.8.1 */
task_declaration /* IEEE1800-2005: A.2.7 */
: K_task lifetime_opt IDENTIFIER ';'
: K_task lifetime_opt identifier_name ';'
{ assert(current_task == 0);
current_task = pform_push_task_scope(@1, $3, $2);
}
@ -2469,7 +2469,7 @@ task_declaration /* IEEE1800-2005: A.2.7 */
delete[]$3;
}
| K_task lifetime_opt IDENTIFIER '('
| K_task lifetime_opt identifier_name '('
{ assert(current_task == 0);
current_task = pform_push_task_scope(@1, $3, $2);
}
@ -2498,7 +2498,7 @@ task_declaration /* IEEE1800-2005: A.2.7 */
delete[]$3;
}
| K_task lifetime_opt IDENTIFIER error K_endtask
| K_task lifetime_opt identifier_name error K_endtask
{
if (current_task) {
pform_pop_scope();
@ -4468,7 +4468,7 @@ hierarchy_identifier
$$->push_back(name_component_t(lex_strings.make($1)));
delete[]$1;
}
| hierarchy_identifier '.' IDENTIFIER
| hierarchy_identifier '.' identifier_name
{ pform_name_t * tmp = $1;
tmp->push_back(name_component_t(lex_strings.make($3)));
delete[]$3;

View File

@ -2496,8 +2496,8 @@ extern "C" ivl_nexus_t ivl_signal_nex(ivl_signal_t net, unsigned word)
if (net->pins) {
return net->pins[word];
} else {
// net->pins can be NULL for a virtualized reg array.
assert(net->type_ == IVL_SIT_REG);
// net->pins can be NULL for a virtualized reg or uwire array.
assert(net->type_ == IVL_SIT_REG || net->type_ == IVL_SIT_UWIRE);
return NULL;
}
} else {

View File

@ -92,6 +92,10 @@ int target_design(ivl_design_t des)
// multiple root scopes, we will give isolated numbers for
// each and keep then separate.
for (unsigned idx = 0 ; idx < nroots ; idx += 1) {
// Skip SystemVerilog $unit scope (compilation unit scope)
if (ivl_scope_type(roots[idx]) == IVL_SCT_PACKAGE) {
continue;
}
if (ivl_scope_type(roots[idx]) != IVL_SCT_MODULE) {
fprintf(stderr, "SIZER: The root scope %s must be a module.\n", ivl_scope_basename(roots[idx]));
sizer_errors += 1;

View File

@ -30,6 +30,8 @@ static ivl_signal_type_t signal_type_of_nexus(ivl_nexus_t nex)
{
unsigned idx;
ivl_signal_type_t out = IVL_SIT_TRI;
int has_tri = 0;
int has_uwire = 0;
for (idx = 0 ; idx < ivl_nexus_ptrs(nex) ; idx += 1) {
ivl_signal_type_t stype;
@ -41,14 +43,29 @@ static ivl_signal_type_t signal_type_of_nexus(ivl_nexus_t nex)
stype = ivl_signal_type(sig);
if (stype == IVL_SIT_REG)
continue;
if (stype == IVL_SIT_TRI)
if (stype == IVL_SIT_TRI) {
has_tri = 1;
continue;
}
if (stype == IVL_SIT_NONE)
continue;
if (stype == IVL_SIT_UWIRE) return IVL_SIT_UWIRE;
if (stype == IVL_SIT_UWIRE) {
has_uwire = 1;
continue;
}
out = stype;
}
/* If both TRI (wire) and UWIRE are in the nexus, return TRI
because wire semantics allow multiple drivers. Only return
UWIRE if no TRI signals are present. This fixes GitHub #1267
where wire logic connected to uwire ports was incorrectly
treated as requiring single-driver semantics.
TODO: Decide how resolved net types (tri0/tri1/triand/trior)
should interact with uwire in a shared nexus. */
if (has_uwire && !has_tri)
return IVL_SIT_UWIRE;
return out;
}