Fix lost writes when select width exceeds variable width (#7975)
Signed-off-by: Bartosz Skorowski <bskorowski@internships.antmicro.com>
This commit is contained in:
parent
a799fedea6
commit
ee152be17b
|
|
@ -1106,6 +1106,8 @@ class WidthVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
UASSERT_OBJ(nodep->dtypep(), nodep, "dtype wasn't set"); // by V3WidthSel
|
UASSERT_OBJ(nodep->dtypep(), nodep, "dtype wasn't set"); // by V3WidthSel
|
||||||
|
|
||||||
|
AstNodeVarRef* lrefp = AstNodeVarRef::varRefLValueRecurse(nodep);
|
||||||
|
const bool isWriteSelect = lrefp && lrefp->access().isWriteOrRW();
|
||||||
// Suppress SELRANGE in parameterized template modules where
|
// Suppress SELRANGE in parameterized template modules where
|
||||||
// parameter-dependent widths haven't been resolved yet.
|
// parameter-dependent widths haven't been resolved yet.
|
||||||
const bool inParameterizedTemplate
|
const bool inParameterizedTemplate
|
||||||
|
|
@ -1124,13 +1126,21 @@ class WidthVisitor final : public VNVisitor {
|
||||||
}
|
}
|
||||||
// We're extracting, so just make sure the expression is at least wide enough.
|
// We're extracting, so just make sure the expression is at least wide enough.
|
||||||
if (nodep->fromp()->width() < width && !inParameterizedTemplate) {
|
if (nodep->fromp()->width() < width && !inParameterizedTemplate) {
|
||||||
nodep->v3warn(SELRANGE, "Extracting " << width << " bits from only "
|
// If it is not a lvalue, extend it
|
||||||
<< nodep->fromp()->width() << " bit number");
|
if (!isWriteSelect) {
|
||||||
// Extend it.
|
nodep->v3warn(SELRANGE, "Extracting " << width << " bits from only "
|
||||||
AstNodeDType* const subDTypep
|
<< nodep->fromp()->width()
|
||||||
= nodep->findLogicDType(width, width, nodep->fromp()->dtypep()->numeric());
|
<< " bit number");
|
||||||
widthCheckSized(nodep, "errorless...", nodep->fromp(), subDTypep, EXTEND_EXP,
|
AstNodeDType* const subDTypep
|
||||||
false /*noerror*/);
|
= nodep->findLogicDType(width, width, nodep->fromp()->dtypep()->numeric());
|
||||||
|
widthCheckSized(nodep, "errorless...", nodep->fromp(), subDTypep, EXTEND_EXP,
|
||||||
|
false /*noerror*/);
|
||||||
|
} else {
|
||||||
|
// Partial assignment
|
||||||
|
nodep->v3warn(SELRANGE, "Assigning " << width << " bits to only "
|
||||||
|
<< nodep->fromp()->width()
|
||||||
|
<< " bit number");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Check bit indexes.
|
// Check bit indexes.
|
||||||
// What is the MSB? We want the true MSB, not one starting at
|
// What is the MSB? We want the true MSB, not one starting at
|
||||||
|
|
@ -1175,26 +1185,26 @@ class WidthVisitor final : public VNVisitor {
|
||||||
// evaluating type sizes for a generate block condition. We
|
// evaluating type sizes for a generate block condition. We
|
||||||
// should only trigger the error if the out-of-range access is
|
// should only trigger the error if the out-of-range access is
|
||||||
// actually generated.
|
// actually generated.
|
||||||
AstNodeVarRef* lrefp = AstNodeVarRef::varRefLValueRecurse(nodep);
|
|
||||||
if (m_doGenerate) {
|
if (m_doGenerate) {
|
||||||
UINFO(5, "Selection index out of range inside generate");
|
UINFO(5, "Selection index out of range inside generate");
|
||||||
} else if (!inParameterizedTemplate) {
|
} else if (!inParameterizedTemplate) {
|
||||||
nodep->v3warn(SELRANGE, "Selection index out of range: "
|
if (nodep->declRange().ranged()) {
|
||||||
<< nodep->msbConst() << ":" << nodep->lsbConst()
|
nodep->v3warn(SELRANGE, "Selection index out of range: "
|
||||||
<< " outside " << frommsb << ":" << fromlsb);
|
<< nodep->msbConst() << ":"
|
||||||
|
<< nodep->lsbConst() << " outside " << frommsb
|
||||||
|
<< ":" << fromlsb);
|
||||||
|
} else {
|
||||||
|
nodep->v3warn(SELRANGE,
|
||||||
|
"Selection "
|
||||||
|
<< nodep->msbConst() << ":" << nodep->lsbConst()
|
||||||
|
<< " performed on an object without declared range");
|
||||||
|
}
|
||||||
UINFO(1, " Related node: " << nodep);
|
UINFO(1, " Related node: " << nodep);
|
||||||
}
|
}
|
||||||
if (lrefp) UINFO(9, " Select extend lrefp " << lrefp);
|
if (lrefp) UINFO(9, " Select extend lrefp " << lrefp);
|
||||||
if (lrefp && lrefp->access().isWriteOrRW()) {
|
// Extend unless it's a lvalue,
|
||||||
// lvarref[X] = ..., the expression assigned is too wide
|
// because extending lvalue would lose write access.
|
||||||
// WTF to do
|
if (!isWriteSelect) {
|
||||||
// Don't change the width of this lhsp, instead propagate up
|
|
||||||
// to upper assign/expression the correct width
|
|
||||||
AstNodeDType* const subDTypep
|
|
||||||
= nodep->findLogicDType(width, width, nodep->fromp()->dtypep()->numeric());
|
|
||||||
widthCheckSized(nodep, "errorless...", nodep->fromp(), subDTypep, EXTEND_EXP,
|
|
||||||
false /*noerror*/);
|
|
||||||
} else {
|
|
||||||
// Extend it
|
// Extend it
|
||||||
const int extendTo = nodep->msbConst() + 1;
|
const int extendTo = nodep->msbConst() + 1;
|
||||||
AstNodeDType* const subDTypep = nodep->findLogicDType(
|
AstNodeDType* const subDTypep = nodep->findLogicDType(
|
||||||
|
|
|
||||||
|
|
@ -3,17 +3,21 @@
|
||||||
15 | dimn[1:0] = 0;
|
15 | dimn[1:0] = 0;
|
||||||
| ^
|
| ^
|
||||||
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance.
|
||||||
%Warning-SELRANGE: t/t_mem_multi_ref_bad.v:15:9: Extracting 2 bits from only 1 bit number
|
%Warning-SELRANGE: t/t_mem_multi_ref_bad.v:15:9: Assigning 2 bits to only 1 bit number
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
15 | dimn[1:0] = 0;
|
15 | dimn[1:0] = 0;
|
||||||
| ^
|
| ^
|
||||||
... For warning description see https://verilator.org/warn/SELRANGE?v=latest
|
... For warning description see https://verilator.org/warn/SELRANGE?v=latest
|
||||||
... Use "/* verilator lint_off SELRANGE */" and lint_on around source to disable this message.
|
... Use "/* verilator lint_off SELRANGE */" and lint_on around source to disable this message.
|
||||||
|
%Warning-SELRANGE: t/t_mem_multi_ref_bad.v:15:9: Selection 1:0 performed on an object without declared range
|
||||||
|
: ... note: In instance 't'
|
||||||
|
15 | dimn[1:0] = 0;
|
||||||
|
| ^
|
||||||
%Error: t/t_mem_multi_ref_bad.v:16:12: Illegal bit or array select; type does not have a bit range, or bad dimension: data type is 'logic'
|
%Error: t/t_mem_multi_ref_bad.v:16:12: Illegal bit or array select; type does not have a bit range, or bad dimension: data type is 'logic'
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
16 | dim0[1][1] = 0;
|
16 | dim0[1][1] = 0;
|
||||||
| ^
|
| ^
|
||||||
%Warning-SELRANGE: t/t_mem_multi_ref_bad.v:16:12: Selection index out of range: 1:1 outside 0:0
|
%Warning-SELRANGE: t/t_mem_multi_ref_bad.v:16:12: Selection 1:1 performed on an object without declared range
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
16 | dim0[1][1] = 0;
|
16 | dim0[1][1] = 0;
|
||||||
| ^
|
| ^
|
||||||
|
|
@ -21,7 +25,7 @@
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
17 | dim1[1][1][1] = 0;
|
17 | dim1[1][1][1] = 0;
|
||||||
| ^
|
| ^
|
||||||
%Warning-SELRANGE: t/t_mem_multi_ref_bad.v:17:15: Selection index out of range: 1:1 outside 0:0
|
%Warning-SELRANGE: t/t_mem_multi_ref_bad.v:17:15: Selection 1:1 performed on an object without declared range
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
17 | dim1[1][1][1] = 0;
|
17 | dim1[1][1][1] = 0;
|
||||||
| ^
|
| ^
|
||||||
|
|
@ -33,7 +37,7 @@
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
23 | dim0nv[1][1] = 0;
|
23 | dim0nv[1][1] = 0;
|
||||||
| ^
|
| ^
|
||||||
%Warning-SELRANGE: t/t_mem_multi_ref_bad.v:23:14: Selection index out of range: 1:1 outside 0:0
|
%Warning-SELRANGE: t/t_mem_multi_ref_bad.v:23:14: Selection 1:1 performed on an object without declared range
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
23 | dim0nv[1][1] = 0;
|
23 | dim0nv[1][1] = 0;
|
||||||
| ^
|
| ^
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it
|
||||||
|
# under the terms of either the GNU Lesser General Public License Version 3
|
||||||
|
# or the Perl Artistic License Version 2.0.
|
||||||
|
# SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
import vltest_bootstrap
|
||||||
|
|
||||||
|
test.scenarios('simulator')
|
||||||
|
|
||||||
|
test.compile()
|
||||||
|
test.execute()
|
||||||
|
|
||||||
|
test.passes()
|
||||||
|
|
@ -0,0 +1,51 @@
|
||||||
|
// DESCRIPTION: Check that a blocking partial write with select outside of MSB
|
||||||
|
// of a 2-state vector doesn't corrup lvalue write refs.
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||||
|
// SPDX-FileCopyrightText: 2026 Antmicro
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
// verilog_format: off
|
||||||
|
`define stop $stop
|
||||||
|
`define checkh(gotv, expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: $time=%0t got='h%x exp='h%x\n", `__FILE__,`__LINE__, $time, (gotv), (expv)); `stop; end while(0)
|
||||||
|
// verilog_format: on
|
||||||
|
|
||||||
|
module t;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
bit [5:0] x;
|
||||||
|
integer i;
|
||||||
|
|
||||||
|
// verilator lint_off SELRANGE
|
||||||
|
x = 'h0;
|
||||||
|
x[8:4] = 5'b10001;
|
||||||
|
`checkh(x, 6'b010000); // Const, partially OOB high
|
||||||
|
|
||||||
|
x = 'h0;
|
||||||
|
x[11:6] = 6'b111111;
|
||||||
|
`checkh(x, '0); // Const, fully OOB high
|
||||||
|
|
||||||
|
x = 'h0;
|
||||||
|
x[9:3] = 7'b1011010;
|
||||||
|
`checkh(x, 6'b010000); // Const, select width > declared width, OOB high
|
||||||
|
|
||||||
|
i = 4;
|
||||||
|
x = 'h0;
|
||||||
|
x[i+:5] = 5'b10001;
|
||||||
|
`checkh(x, 6'b010000); // Var, partially OOB high
|
||||||
|
|
||||||
|
i = 6;
|
||||||
|
x = 'h0;
|
||||||
|
x[i+:6] = 6'b111111;
|
||||||
|
`checkh(x, '0); // Var, fully OOB high
|
||||||
|
|
||||||
|
i = 3;
|
||||||
|
x = 'h0;
|
||||||
|
x[i+:7] = 7'b1011010;
|
||||||
|
`checkh(x, 6'b010000); // Var, select width > declared width, OOB high
|
||||||
|
// verilator lint_on SELRANGE
|
||||||
|
|
||||||
|
$write("*-* All finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
| ^
|
| ^
|
||||||
... For warning description see https://verilator.org/warn/SELRANGE?v=latest
|
... For warning description see https://verilator.org/warn/SELRANGE?v=latest
|
||||||
... Use "/* verilator lint_off SELRANGE */" and lint_on around source to disable this message.
|
... Use "/* verilator lint_off SELRANGE */" and lint_on around source to disable this message.
|
||||||
%Warning-SELRANGE: t/t_select_bad_range5.v:18:18: Selection index out of range: 3:2 outside 1:0
|
%Warning-SELRANGE: t/t_select_bad_range5.v:18:18: Selection 3:2 performed on an object without declared range
|
||||||
: ... note: In instance 't'
|
: ... note: In instance 't'
|
||||||
18 | assign mi = unk[3:2];
|
18 | assign mi = unk[3:2];
|
||||||
| ^
|
| ^
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue