setundef: strip init attributes from undriven wires (fixes #5835)

When `setundef -undriven` connects an undriven wire to a replacement
value, the wire's \\init attribute (if present) is now removed. Previously,
the init attribute was left intact, causing downstream passes like
opt_merge to report "Conflicting init values" errors because the init
value contradicted the newly assigned constant.

For wires that are entirely undriven, the init attribute is removed
completely. For partially undriven wires (where only some bits are
undriven), only the corresponding init bits are cleared to x.

Wires driven by flip-flops or other cells are not affected, as they
are excluded from the undriven signal set before this code runs.
This commit is contained in:
junyao 2026-05-31 00:18:49 +08:00
parent e56c6a954c
commit 46f9f887f7
2 changed files with 83 additions and 0 deletions

View File

@ -355,6 +355,28 @@ struct SetundefPass : public Pass {
bits.append(worker.next_bit());
module->connect(RTLIL::SigSig(c, bits));
}
// Remove init attributes from undriven wires to prevent
// conflicts with the values we just assigned (issue #5835).
for (auto &c : sig.chunks()) {
if (c.wire && c.wire->attributes.count(ID::init)) {
if (c.wire->width == c.width && c.offset == 0) {
c.wire->attributes.erase(ID::init);
log("Removing init attribute from undriven wire %s.\n", log_id(c.wire));
} else {
Const &initval = c.wire->attributes[ID::init];
initval.resize(GetSize(c.wire), State::Sx);
for (int i = c.offset; i < c.offset + c.width; i++)
initval.set(i, State::Sx);
if (initval.is_fully_undef()) {
c.wire->attributes.erase(ID::init);
log("Removing init attribute from undriven wire %s.\n", log_id(c.wire));
} else {
log("Clearing init attribute bits [%d:%d] from partially undriven wire %s.\n",
c.offset + c.width - 1, c.offset, log_id(c.wire));
}
}
}
}
}
}

View File

@ -0,0 +1,61 @@
# Test for issue #5835: setundef -undriven should not cause conflicts
# with init attributes on undriven wires.
# Test 1: Basic case from the bug report - undriven wire with init attribute
read_rtlil << EOT
module \top
attribute \init 3
wire width 4 \i
end
EOT
setundef -undriven -undef
# Verify that the init attribute was removed from the undriven wire
select -assert-count 0 w:* a:init %i
# Verify that opt doesn't crash with "Conflicting init values" error
opt
design -reset
# Test 2: setundef -undriven -zero with init attribute
read_rtlil << EOT
module \top
attribute \init 3
wire width 4 \i
end
EOT
setundef -undriven -zero
select -assert-count 0 w:* a:init %i
opt
design -reset
# Test 3: setundef -undriven -one with init attribute
read_rtlil << EOT
module \top
attribute \init 3
wire width 4 \i
end
EOT
setundef -undriven -one
select -assert-count 0 w:* a:init %i
opt
design -reset
# Test 4: Wire driven by a cell should keep its init attribute
read_rtlil << EOT
module \top
wire width 1 input 1 \clk
wire width 1 input 2 \d
attribute \init 1'0
wire width 1 output 3 \q
cell $dff \myff
parameter \CLK_POLARITY 1'1
parameter \WIDTH 1
connect \CLK \clk
connect \D \d
connect \Q \q
end
end
EOT
setundef -undriven -zero
# The init attribute should still be present since the wire is driven by a FF
select -assert-count 1 w:* a:init %i
design -reset