This commit is contained in:
parent
de4f743d0e
commit
c518abd22a
|
|
@ -158,22 +158,21 @@ private:
|
|||
return static_cast<T>((cur & ~mask) | (rhsBits & mask));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static typename std::enable_if<VlIsVlWide<T>::value, T>::type applyEntry(T result,
|
||||
const Entry& entry) {
|
||||
EData* const reswp = result.data();
|
||||
const int lword = VL_BITWORD_E(entry.m_lsb);
|
||||
const int hword = VL_BITWORD_E(entry.m_msb);
|
||||
static void applyEntry(WDataOutP reswp, const Entry& entry, int entryLsb, int entryMsb,
|
||||
int lsbOffset) {
|
||||
const int resLsb = entryLsb - lsbOffset;
|
||||
const int resMsb = entryMsb - lsbOffset;
|
||||
const int lword = VL_BITWORD_E(resLsb);
|
||||
const int hword = VL_BITWORD_E(resMsb);
|
||||
for (int word = lword; word <= hword; ++word) {
|
||||
const int wordLsb = word * VL_EDATASIZE;
|
||||
const int segLsb = std::max(entry.m_lsb, wordLsb);
|
||||
const int segMsb = std::min(entry.m_msb, wordLsb + VL_EDATASIZE - 1);
|
||||
const int segLsb = std::max(resLsb, wordLsb);
|
||||
const int segMsb = std::min(resMsb, wordLsb + VL_EDATASIZE - 1);
|
||||
const int segWidth = segMsb - segLsb + 1;
|
||||
const int bitOffset = segLsb - wordLsb;
|
||||
const int rhsLsb = segLsb - entry.m_rhsLsb;
|
||||
const int rhsLsb = lsbOffset + segLsb - entry.m_rhsLsb;
|
||||
reswp[word] = applyBits(reswp[word], entry, bitOffset, segWidth, rhsLsb);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
|
|
@ -196,6 +195,29 @@ private:
|
|||
return *static_cast<const VlForceBaseType<T>*>(entry.m_rhsDatap);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<VlIsVlWide<T>::value>::type applyEntries(T& val) const {
|
||||
for (const auto& entry : m_entries) {
|
||||
applyEntry(val.data(), entry, entry.m_lsb, entry.m_msb, 0);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<!VlIsVlWide<T>::value>::type applyEntries(T& val) const {
|
||||
for (const auto& entry : m_entries) val = applyEntry(val, entry);
|
||||
}
|
||||
|
||||
void readSel(int lbits, WDataInP valp, WDataOutP reswp, int lsb, int width) const {
|
||||
VL_SEL_WWII(width, lbits, reswp, valp, lsb, width);
|
||||
const int msb = lsb + width - 1;
|
||||
auto it = std::lower_bound(m_entries.begin(), m_entries.end(), lsb,
|
||||
[](const Entry& e, int bit) { return e.m_msb < bit; });
|
||||
while (it != m_entries.end() && it->m_lsb <= msb) {
|
||||
applyEntry(reswp, *it, std::max(it->m_lsb, lsb), std::min(it->m_msb, msb), lsb);
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
VlForceVec() = default;
|
||||
|
||||
|
|
@ -219,8 +241,7 @@ public:
|
|||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
for (const auto& entry : m_entries) { val = applyEntry(val, entry); }
|
||||
applyEntries(val);
|
||||
return val;
|
||||
}
|
||||
|
||||
|
|
@ -238,6 +259,29 @@ public:
|
|||
return origVal;
|
||||
}
|
||||
|
||||
IData readSelI(int lbits, WDataInP valp, int lsb, int width) const {
|
||||
IData result;
|
||||
readSel(lbits, valp, &result, lsb, width);
|
||||
result &= VL_MASK_I(width);
|
||||
return result;
|
||||
}
|
||||
|
||||
QData readSelQ(int lbits, WDataInP valp, int lsb, int width) const {
|
||||
QData result;
|
||||
readSel(lbits, valp, reinterpret_cast<WDataOutP>(&result), lsb, width);
|
||||
result &= VL_MASK_Q(width);
|
||||
return result;
|
||||
}
|
||||
|
||||
template <std::size_t N_Words>
|
||||
VlWide<N_Words> readSelW(int lbits, WDataInP valp, int lsb, int width) const {
|
||||
VlWide<N_Words> result;
|
||||
WDataOutP const reswp = result.data();
|
||||
readSel(lbits, valp, reswp, lsb, width);
|
||||
reswp[N_Words - 1] &= VL_MASK_E(width);
|
||||
return result;
|
||||
}
|
||||
|
||||
void addForce(int lsb, int msb, const void* rhsDatap, int rhsLsb) {
|
||||
assert(lsb <= msb);
|
||||
assert(rhsDatap);
|
||||
|
|
|
|||
|
|
@ -815,6 +815,7 @@ public:
|
|||
FORCE_ADD,
|
||||
FORCE_READ,
|
||||
FORCE_READ_INDEX,
|
||||
FORCE_READ_SEL,
|
||||
FORCE_RELEASE,
|
||||
FORCE_TOUCH,
|
||||
FORK_DONE,
|
||||
|
|
@ -968,6 +969,7 @@ inline std::ostream& operator<<(std::ostream& os, const VCMethod& rhs) {
|
|||
{FORCE_ADD, "addForce", false}, \
|
||||
{FORCE_READ, "read", true}, \
|
||||
{FORCE_READ_INDEX, "readIndex", true}, \
|
||||
{FORCE_READ_SEL, "readSel", true}, \
|
||||
{FORCE_RELEASE, "release", false}, \
|
||||
{FORCE_TOUCH, "touch", false}, \
|
||||
{FORK_DONE, "done", false}, \
|
||||
|
|
|
|||
|
|
@ -723,6 +723,10 @@ public:
|
|||
iterateConst(nodep->fromp());
|
||||
putns(nodep, nodep->usePtr() ? "->" : ".");
|
||||
putns(nodep, nodep->name());
|
||||
if (nodep->method() == VCMethod::FORCE_READ_SEL) {
|
||||
emitIQW(nodep);
|
||||
if (nodep->isWide()) puts("<" + cvtToStr(nodep->dtypep()->widthWords()) + ">");
|
||||
}
|
||||
puts("(");
|
||||
bool comma = false;
|
||||
int argNum = 0;
|
||||
|
|
|
|||
|
|
@ -1088,6 +1088,33 @@ class ForceReplaceVisitor final : public VNVisitor {
|
|||
iterateLogic(nodep);
|
||||
}
|
||||
void visit(AstSenItem* nodep) override { iterateLogic(nodep); }
|
||||
void visit(AstSel* nodep) override {
|
||||
// Replace Sel on a wide with readSelI/Q/W to avoid materializing the full value
|
||||
if (AstVarRef* const refp = VN_CAST(nodep->fromp(), VarRef)) {
|
||||
if (!ForceState::isNotReplaceable(refp) && refp->access().isReadOnly()) {
|
||||
AstVar* const varp = refp->varp();
|
||||
const ForceState::VarForceInfo* const varInfo = m_state.getVarInfo(varp);
|
||||
if (varInfo && !varInfo->m_forceRdVscp && !varInfo->m_forces.empty()
|
||||
&& ForceState::isBitwiseDType(varp) && varp->dtypep()->isWide()) {
|
||||
FileLine* const flp = nodep->fileline();
|
||||
ForceState::markNonReplaceable(refp);
|
||||
AstVarRef* const refClonep = refp->cloneTreePure(false);
|
||||
ForceState::markNonReplaceable(refClonep);
|
||||
AstCMethodHard* const callp = new AstCMethodHard{
|
||||
flp, new AstVarRef{flp, varInfo->m_forceVecVscp, VAccess::READ},
|
||||
VCMethod::FORCE_READ_SEL, ForceState::makeConst32(flp, varp->width())};
|
||||
callp->addPinsp(refClonep);
|
||||
callp->addPinsp(nodep->lsbp()->cloneTreePure(false));
|
||||
callp->addPinsp(ForceState::makeConst32(flp, nodep->width()));
|
||||
callp->dtypeFrom(nodep);
|
||||
nodep->replaceWith(callp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
visit(static_cast<AstNode*>(nodep));
|
||||
}
|
||||
void visit(AstArraySel* nodep) override {
|
||||
if (nodep->backp() && VN_IS(nodep->backp(), ArraySel)) {
|
||||
// Only the outermost unpacked array selection should become a force-aware read;
|
||||
|
|
|
|||
|
|
@ -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,42 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// 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: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
|
||||
// verilog_format: on
|
||||
|
||||
module t (
|
||||
input clk
|
||||
);
|
||||
integer cyc = 0;
|
||||
logic [127:0] sig;
|
||||
|
||||
always @(posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
sig <= '0;
|
||||
|
||||
sig[127:64] <= '0; // write path
|
||||
|
||||
if (cyc == 1) begin
|
||||
force sig[31] = 1'b1;
|
||||
force sig[32] = 1'b1;
|
||||
end
|
||||
else if (cyc == 3) begin
|
||||
`checkh(sig[33:26], 8'h60); // width <= 8
|
||||
`checkh(sig[39:24], 16'h180); // 8 < width <= 16
|
||||
`checkh(sig[40:20], 21'h1800); // 16 < width <= 32
|
||||
`checkh(sig[51:20], 32'h1800);
|
||||
`checkh(sig[29:0], 30'h0);
|
||||
`checkh(sig[50:10], 41'h600000); // 32 < width <= 64
|
||||
`checkh(sig[73:10], 64'h600000);
|
||||
`checkh(sig[100:5], (96'h1 << 26) | (96'h1 << 27)); // width > 64
|
||||
`checkh(sig[70:6], (65'h1 << 25) | (65'h1 << 26));
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue