This commit is contained in:
parent
7e67f73844
commit
25f72e4305
216
src/V3Force.cpp
216
src/V3Force.cpp
|
|
@ -54,6 +54,7 @@ VL_DEFINE_DEBUG_FUNCTIONS;
|
|||
// Convert force/release statements and signals marked 'forceable'
|
||||
|
||||
class ForceState final {
|
||||
constexpr static int ELEMENTS_MAX = 1000;
|
||||
// TYPES
|
||||
struct ForceComponentsVar final {
|
||||
AstVar* const m_rdVarp; // New variable to replace read references with
|
||||
|
|
@ -64,9 +65,8 @@ class ForceState final {
|
|||
varp->dtypep()}}
|
||||
, m_valVarp{new AstVar{varp->fileline(), VVarType::VAR, varp->name() + "__VforceVal",
|
||||
varp->dtypep()}}
|
||||
, m_enVarp{new AstVar{
|
||||
varp->fileline(), VVarType::VAR, varp->name() + "__VforceEn",
|
||||
(ForceState::isRangedDType(varp) ? varp->dtypep() : varp->findBitDType())}} {
|
||||
, m_enVarp{new AstVar{varp->fileline(), VVarType::VAR, varp->name() + "__VforceEn",
|
||||
getEnVarpDTypep(varp)}} {
|
||||
m_rdVarp->addNext(m_enVarp);
|
||||
m_rdVarp->addNext(m_valVarp);
|
||||
varp->addNextHere(m_rdVarp);
|
||||
|
|
@ -88,24 +88,70 @@ public:
|
|||
|
||||
FileLine* const flp = vscp->fileline();
|
||||
|
||||
{ // Add initialization of the enable signal
|
||||
AstVarRef* const lhsp = new AstVarRef{flp, m_enVscp, VAccess::WRITE};
|
||||
V3Number zero{m_enVscp, m_enVscp->width()};
|
||||
zero.setAllBits0();
|
||||
AstNodeExpr* const rhsp = new AstConst{flp, zero};
|
||||
AstAssign* const assignp = new AstAssign{flp, lhsp, rhsp};
|
||||
AstActive* const activep = new AstActive{
|
||||
flp, "force-init",
|
||||
new AstSenTree{flp, new AstSenItem{flp, AstSenItem::Static{}}}};
|
||||
activep->senTreeStorep(activep->sentreep());
|
||||
// Add initialization of the enable signal
|
||||
AstActive* const activeInitp = new AstActive{
|
||||
flp, "force-init", new AstSenTree{flp, new AstSenItem{flp, AstSenItem::Static{}}}};
|
||||
activeInitp->senTreeStorep(activeInitp->sentreep());
|
||||
vscp->scopep()->addBlocksp(activeInitp);
|
||||
|
||||
activep->addStmtsp(new AstInitial{flp, assignp});
|
||||
vscp->scopep()->addBlocksp(activep);
|
||||
AstVarRef* const enRefp = new AstVarRef{flp, m_enVscp, VAccess::WRITE};
|
||||
|
||||
AstNodeStmt* toInsertp = nullptr;
|
||||
AstNodeStmt* outerStmtp = nullptr;
|
||||
std::vector<AstNodeExpr*> loopVarRefs;
|
||||
if (VN_IS(enRefp->dtypep()->skipRefp(), UnpackArrayDType)) {
|
||||
// Create a loop to set all elements of __VforceEn array to 0.
|
||||
// That loop node is then copied and used for updating elements of __VforceRd array
|
||||
if (AstUnpackArrayDType* const unpackedp
|
||||
= VN_CAST(m_rdVscp->varp()->dtypep(), UnpackArrayDType)) {
|
||||
std::vector<AstUnpackArrayDType*> dims = unpackedp->unpackDimensions();
|
||||
loopVarRefs.reserve(dims.size());
|
||||
for (size_t i = 0; i < dims.size(); i++) {
|
||||
AstVar* const loopVarp = new AstVar{
|
||||
flp, VVarType::MODULETEMP,
|
||||
m_rdVscp->varp()->name() + "__VwhileIter" + std::to_string(i),
|
||||
VFlagBitPacked{}, 32};
|
||||
m_rdVscp->varp()->addNext(loopVarp);
|
||||
AstVarScope* const loopVarScopep
|
||||
= new AstVarScope{flp, m_rdVscp->scopep(), loopVarp};
|
||||
m_rdVscp->addNext(loopVarScopep);
|
||||
AstVarRef* const readRefp
|
||||
= new AstVarRef{flp, loopVarScopep, VAccess::READ};
|
||||
loopVarRefs.push_back(readRefp);
|
||||
AstNodeStmt* const currInitp
|
||||
= new AstAssign{flp, new AstVarRef{flp, loopVarScopep, VAccess::WRITE},
|
||||
new AstConst{flp, 0}};
|
||||
if (toInsertp) {
|
||||
toInsertp->addNextHere(currInitp);
|
||||
} else {
|
||||
outerStmtp = currInitp;
|
||||
}
|
||||
AstLoop* const currWhilep = new AstLoop{flp};
|
||||
currInitp->addNextHere(currWhilep);
|
||||
AstLoopTest* const loopTestp = new AstLoopTest{
|
||||
flp, currWhilep,
|
||||
new AstNeq{flp, readRefp,
|
||||
new AstConst{
|
||||
flp, static_cast<uint32_t>(dims[i]->elementsConst())}}};
|
||||
currWhilep->addStmtsp(loopTestp);
|
||||
toInsertp = loopTestp;
|
||||
AstAssign* const currIncrp = new AstAssign{
|
||||
flp, new AstVarRef{flp, loopVarScopep, VAccess::WRITE},
|
||||
new AstAdd{flp, readRefp->cloneTree(false), new AstConst{flp, 1}}};
|
||||
currWhilep->addStmtsp(currIncrp);
|
||||
}
|
||||
}
|
||||
}
|
||||
V3Number zero{m_enVscp, m_enVscp->width()};
|
||||
AstNodeExpr* const enRhsp = new AstConst{flp, zero};
|
||||
AstNodeExpr* enLhsp = applySelects(enRefp, loopVarRefs);
|
||||
AstNodeStmt* stmtp = new AstAssign{flp, enLhsp, enRhsp};
|
||||
if (toInsertp) {
|
||||
toInsertp->addNextHere(stmtp);
|
||||
stmtp = outerStmtp;
|
||||
}
|
||||
activeInitp->addStmtsp(new AstInitial{flp, stmtp->cloneTree(true)});
|
||||
{ // Add the combinational override
|
||||
AstVarRef* const lhsp = new AstVarRef{flp, m_rdVscp, VAccess::WRITE};
|
||||
AstNodeExpr* const rhsp = forcedUpdate(vscp);
|
||||
|
||||
// Explicitly list dependencies for update.
|
||||
// Note: rdVscp is also needed to retrigger assignment for the first time.
|
||||
AstSenItem* const itemsp = new AstSenItem{
|
||||
|
|
@ -120,25 +166,49 @@ public:
|
|||
AstActive* const activep
|
||||
= new AstActive{flp, "force-update", new AstSenTree{flp, itemsp}};
|
||||
activep->senTreeStorep(activep->sentreep());
|
||||
activep->addStmtsp(new AstAlways{flp, VAlwaysKwd::ALWAYS, nullptr,
|
||||
new AstAssign{flp, lhsp, rhsp}});
|
||||
|
||||
// Reuse the statements created for __VforceEn initialization
|
||||
// and replace var ref on the LHS and the RHS
|
||||
AstVarRef* const rdRefp = new AstVarRef{flp, m_rdVscp, VAccess::WRITE};
|
||||
AstNodeExpr* const rdRhsp = forcedUpdate(vscp, loopVarRefs);
|
||||
enRefp->replaceWith(rdRefp);
|
||||
VL_DO_DANGLING(enRefp->deleteTree(), enRefp);
|
||||
enRhsp->replaceWith(rdRhsp);
|
||||
VL_DO_DANGLING(enRhsp->deleteTree(), enRhsp);
|
||||
|
||||
activep->addStmtsp(new AstAlways{flp, VAlwaysKwd::ALWAYS, nullptr, stmtp});
|
||||
vscp->scopep()->addBlocksp(activep);
|
||||
}
|
||||
}
|
||||
AstNodeExpr* forcedUpdate(AstVarScope* const vscp) const {
|
||||
static AstNodeExpr* applySelects(AstNodeExpr* exprp,
|
||||
const std::vector<AstNodeExpr*>& selectExprs) {
|
||||
for (AstNodeExpr* const sp : selectExprs) {
|
||||
exprp = new AstArraySel{exprp->fileline(), exprp, sp->cloneTreePure(false)};
|
||||
}
|
||||
return exprp;
|
||||
}
|
||||
AstNodeExpr* forcedUpdate(AstVarScope* const vscp,
|
||||
const std::vector<AstNodeExpr*>& selectExprs) const {
|
||||
FileLine* const flp = vscp->fileline();
|
||||
AstVarRef* const origp = new AstVarRef{flp, vscp, VAccess::READ};
|
||||
ForceState::markNonReplaceable(origp);
|
||||
AstVarRef* origRefp = new AstVarRef{flp, vscp, VAccess::READ};
|
||||
ForceState::markNonReplaceable(origRefp);
|
||||
AstNodeExpr* const origp = applySelects(origRefp, selectExprs);
|
||||
if (ForceState::isRangedDType(vscp)) {
|
||||
return new AstOr{
|
||||
flp,
|
||||
new AstAnd{flp, new AstVarRef{flp, m_enVscp, VAccess::READ},
|
||||
new AstVarRef{flp, m_valVscp, VAccess::READ}},
|
||||
new AstAnd{flp, new AstNot{flp, new AstVarRef{flp, m_enVscp, VAccess::READ}},
|
||||
new AstAnd{
|
||||
flp,
|
||||
applySelects(new AstVarRef{flp, m_enVscp, VAccess::READ}, selectExprs),
|
||||
applySelects(new AstVarRef{flp, m_valVscp, VAccess::READ}, selectExprs)},
|
||||
new AstAnd{
|
||||
flp,
|
||||
new AstNot{flp, applySelects(new AstVarRef{flp, m_enVscp, VAccess::READ},
|
||||
selectExprs)},
|
||||
origp}};
|
||||
}
|
||||
return new AstCond{flp, new AstVarRef{flp, m_enVscp, VAccess::READ},
|
||||
new AstVarRef{flp, m_valVscp, VAccess::READ}, origp};
|
||||
return new AstCond{
|
||||
flp, applySelects(new AstVarRef{flp, m_enVscp, VAccess::READ}, selectExprs),
|
||||
applySelects(new AstVarRef{flp, m_valVscp, VAccess::READ}, selectExprs), origp};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -147,6 +217,7 @@ private:
|
|||
// AstVar::user1p -> ForceComponentsVar* instance (via m_forceComponentsVar)
|
||||
// AstVarScope::user1p -> ForceComponentsVarScope* instance (via m_forceComponentsVarScope)
|
||||
// AstVarRef::user2 -> Flag indicating not to replace reference
|
||||
// AstAssign::user2 -> Flag indicating that assignment was created for AstRelease
|
||||
// AstVarScope::user3p -> AstAssign*, the assignment <lhs>__VforceVal = <rhs>
|
||||
const VNUser1InUse m_user1InUse;
|
||||
const VNUser2InUse m_user2InUse;
|
||||
|
|
@ -158,6 +229,46 @@ private:
|
|||
m_valVscps;
|
||||
// `valVscp` force components of a forced RHS
|
||||
|
||||
static AstNodeDType* getEnVarpDTypep(AstVar* const varp) {
|
||||
AstNodeDType* const origDTypep = varp->dtypep()->skipRefp();
|
||||
if (VN_IS(origDTypep, UnpackArrayDType)) {
|
||||
size_t elemNum = 1;
|
||||
AstNodeDType* dtp = origDTypep;
|
||||
while (AstUnpackArrayDType* const uDtp = VN_CAST(dtp, UnpackArrayDType)) {
|
||||
dtp = uDtp->subDTypep()->skipRefp();
|
||||
elemNum *= uDtp->elementsConst();
|
||||
}
|
||||
if (elemNum > ELEMENTS_MAX) {
|
||||
varp->v3warn(E_UNSUPPORTED, "Unsupported: Force of unpacked array variable with "
|
||||
">= "
|
||||
<< ELEMENTS_MAX << " elements");
|
||||
}
|
||||
bool complexElem = true;
|
||||
if (AstBasicDType* const basicp = VN_CAST(dtp, BasicDType)) {
|
||||
complexElem = basicp->isOpaque();
|
||||
}
|
||||
if (complexElem) {
|
||||
varp->v3warn(E_UNSUPPORTED, "Unsupported: Force of unpacked array variable with "
|
||||
"elements of complex data type");
|
||||
}
|
||||
return origDTypep;
|
||||
} else if (VN_IS(origDTypep, BasicDType)) {
|
||||
return isRangedDType(varp) ? origDTypep : varp->findBitDType();
|
||||
} else if (VN_IS(origDTypep, PackArrayDType)) {
|
||||
return origDTypep;
|
||||
} else if (const AstNodeUOrStructDType* const sdtp
|
||||
= VN_CAST(origDTypep, NodeUOrStructDType)) {
|
||||
if (!sdtp->packed()) {
|
||||
varp->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: Force of unpacked struct / union variable");
|
||||
}
|
||||
return origDTypep;
|
||||
} else {
|
||||
varp->v3fatalSrc("Unsupported: Force of variable of unhandled data type");
|
||||
return origDTypep;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
ForceState() = default;
|
||||
|
|
@ -292,6 +403,7 @@ class ForceConvertVisitor final : public VNVisitor {
|
|||
// the continuous assignment's scheduling region.
|
||||
AstAssign* const resetRdp
|
||||
= new AstAssign{flp, lhsp->cloneTreePure(false), lhsp->unlinkFrBack()};
|
||||
resetRdp->user2(true);
|
||||
// Replace write refs on the LHS
|
||||
resetRdp->lhsp()->foreach([this](AstVarRef* refp) {
|
||||
if (refp->access() != VAccess::WRITE) return;
|
||||
|
|
@ -304,6 +416,27 @@ class ForceConvertVisitor final : public VNVisitor {
|
|||
}
|
||||
});
|
||||
// Replace write refs on RHS
|
||||
if (VN_IS(resetRdp->rhsp(), ArraySel)) {
|
||||
std::vector<AstNodeExpr*> selIndices;
|
||||
AstNodeExpr* exprp = resetRdp->rhsp();
|
||||
while (AstArraySel* const selp = VN_CAST(exprp, ArraySel)) {
|
||||
selIndices.push_back(selp->bitp());
|
||||
exprp = selp->fromp();
|
||||
}
|
||||
if (AstVarRef* const refp = VN_CAST(exprp, VarRef)) {
|
||||
AstVarScope* const vscp = refp->varScopep();
|
||||
std::vector<AstNodeExpr*> reversedIndices(selIndices.size());
|
||||
std::reverse_copy(selIndices.begin(), selIndices.end(), reversedIndices.begin());
|
||||
AstNodeExpr* const origRhsp = resetRdp->rhsp();
|
||||
origRhsp->replaceWith(
|
||||
m_state.getForceComponents(vscp).forcedUpdate(vscp, reversedIndices));
|
||||
VL_DO_DANGLING(origRhsp->deleteTree(), origRhsp);
|
||||
} else {
|
||||
exprp->v3warn(
|
||||
E_UNSUPPORTED,
|
||||
"Unsupported: Release statement argument is too complex array select");
|
||||
}
|
||||
} else {
|
||||
resetRdp->rhsp()->foreach([this](AstVarRef* refp) {
|
||||
if (refp->access() != VAccess::WRITE) return;
|
||||
AstVarScope* const vscp = refp->varScopep();
|
||||
|
|
@ -311,10 +444,11 @@ class ForceConvertVisitor final : public VNVisitor {
|
|||
refp->access(VAccess::READ);
|
||||
ForceState::markNonReplaceable(refp);
|
||||
} else {
|
||||
refp->replaceWith(m_state.getForceComponents(vscp).forcedUpdate(vscp));
|
||||
refp->replaceWith(m_state.getForceComponents(vscp).forcedUpdate(vscp, {}));
|
||||
VL_DO_DANGLING(refp->deleteTree(), refp);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
resetRdp->addNext(resetEnp);
|
||||
relinker.relink(resetRdp);
|
||||
|
|
@ -344,6 +478,8 @@ class ForceReplaceVisitor final : public VNVisitor {
|
|||
const ForceState& m_state;
|
||||
AstNodeStmt* m_stmtp = nullptr;
|
||||
bool m_inLogic = false;
|
||||
bool m_releaseRhs = false; // Inside RHS of assignment created for release statement
|
||||
std::vector<AstNodeExpr*> m_selIndices; // Indices of array select expressions above
|
||||
|
||||
// METHODS
|
||||
void iterateLogic(AstNode* logicp) {
|
||||
|
|
@ -358,6 +494,14 @@ class ForceReplaceVisitor final : public VNVisitor {
|
|||
m_stmtp = nodep;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstAssign* nodep) override {
|
||||
VL_RESTORER(m_stmtp);
|
||||
VL_RESTORER(m_releaseRhs);
|
||||
m_stmtp = nodep;
|
||||
iterate(nodep->lhsp());
|
||||
m_releaseRhs = nodep->user2();
|
||||
iterate(nodep->rhsp());
|
||||
}
|
||||
void visit(AstCFunc* nodep) override { iterateLogic(nodep); }
|
||||
void visit(AstCoverToggle* nodep) override { iterateLogic(nodep); }
|
||||
void visit(AstNodeProcedure* nodep) override { iterateLogic(nodep); }
|
||||
|
|
@ -371,6 +515,12 @@ class ForceReplaceVisitor final : public VNVisitor {
|
|||
iterateLogic(nodep);
|
||||
}
|
||||
void visit(AstSenItem* nodep) override { iterateLogic(nodep); }
|
||||
void visit(AstArraySel* nodep) override {
|
||||
m_selIndices.push_back(nodep->bitp());
|
||||
iterateChildren(nodep);
|
||||
UASSERT_OBJ(m_selIndices.size(), nodep, "Underflow");
|
||||
m_selIndices.pop_back();
|
||||
}
|
||||
void visit(AstVarRef* nodep) override {
|
||||
if (ForceState::isNotReplaceable(nodep)) return;
|
||||
|
||||
|
|
@ -390,8 +540,12 @@ class ForceReplaceVisitor final : public VNVisitor {
|
|||
if (ForceState::ForceComponentsVarScope* const fcp
|
||||
= m_state.tryGetForceComponents(nodep)) {
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstVarRef* const lhsp = new AstVarRef{flp, fcp->m_rdVscp, VAccess::WRITE};
|
||||
AstNodeExpr* const rhsp = fcp->forcedUpdate(nodep->varScopep());
|
||||
std::vector<AstNodeExpr*> reversedIndices(m_selIndices.size());
|
||||
std::reverse_copy(m_selIndices.begin(), m_selIndices.end(),
|
||||
reversedIndices.begin());
|
||||
AstNodeExpr* const lhsp = ForceState::ForceComponentsVarScope::applySelects(
|
||||
new AstVarRef{flp, fcp->m_rdVscp, VAccess::WRITE}, reversedIndices);
|
||||
AstNodeExpr* const rhsp = fcp->forcedUpdate(nodep->varScopep(), reversedIndices);
|
||||
m_stmtp->addNextHere(new AstAssign{flp, lhsp, rhsp});
|
||||
}
|
||||
// Emit valVscp update after each write to any VarRef on forced RHS.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2024 by Wilson Snyder. 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-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,66 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
`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)
|
||||
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
|
||||
module t ( /*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
|
||||
integer cyc = 0;
|
||||
|
||||
logic logic_arr[2][-2:2][-3:-5];
|
||||
int int_arr[-1:2][1][3];
|
||||
|
||||
// Test loop
|
||||
always @(posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
if (cyc == 0) begin
|
||||
logic_arr[0][2][-4] <= 1;
|
||||
int_arr[0][0][2] <= 1;
|
||||
end
|
||||
else if (cyc == 1) begin
|
||||
`checkh(logic_arr[0][2][-4], 1);
|
||||
`checkh(int_arr[0][0][2], 1);
|
||||
end
|
||||
else if (cyc == 2) begin
|
||||
force logic_arr[0][2][-4] = 0;
|
||||
force int_arr[0][0][2] = 0;
|
||||
end
|
||||
else if (cyc == 3) begin
|
||||
`checkh(logic_arr[0][2][-4], 0);
|
||||
logic_arr[0][2][-4] <= 1;
|
||||
`checkh(int_arr[0][0][2], 0);
|
||||
int_arr[0][0][2] <= 1;
|
||||
end
|
||||
else if (cyc == 4) begin
|
||||
`checkh(logic_arr[0][2][-4], 0);
|
||||
`checkh(int_arr[0][0][2], 0);
|
||||
end
|
||||
else if (cyc == 5) begin
|
||||
release logic_arr[0][2][-4];
|
||||
release int_arr[0][0][2];
|
||||
end
|
||||
else if (cyc == 6) begin
|
||||
`checkh(logic_arr[0][2][-4], 0);
|
||||
logic_arr[0][2][-4] <= 1;
|
||||
`checkh(int_arr[0][0][2], 0);
|
||||
int_arr[0][0][2] <= 1;
|
||||
end
|
||||
else if (cyc == 7) begin
|
||||
`checkh(logic_arr[0][2][-4], 1);
|
||||
`checkh(int_arr[0][0][2], 1);
|
||||
end
|
||||
else if (cyc == 8) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:23:8: Unsupported: Force of unpacked array variable with elements of complex data type
|
||||
23 | real r_array[2];
|
||||
| ^~~~~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:22:7: Unsupported: Force of unpacked array variable with >= 1000 elements
|
||||
22 | bit big_array[40][40][40];
|
||||
| ^~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:21:12: Unsupported: Force of unpacked array variable with >= 1000 elements
|
||||
21 | struct_t s_array[3000];
|
||||
| ^~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:21:12: Unsupported: Force of unpacked array variable with elements of complex data type
|
||||
21 | struct_t s_array[3000];
|
||||
| ^~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_force_unpacked_unsup.v:24:12: Unsupported: Force of unpacked struct / union variable
|
||||
24 | struct_t my_struct;
|
||||
| ^~~~~~~~~
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# Copyright 2024 by Wilson Snyder. 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-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.lint(verilator_flags2=['--timing'], fails=True, expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2025 by Antmicro.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
`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)
|
||||
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
|
||||
module t ( /*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
input clk;
|
||||
|
||||
integer cyc = 0;
|
||||
|
||||
typedef struct {int x;} struct_t;
|
||||
|
||||
struct_t s_array[3000];
|
||||
bit big_array[40][40][40];
|
||||
real r_array[2];
|
||||
struct_t my_struct;
|
||||
|
||||
// Test loop
|
||||
always @(posedge clk) begin
|
||||
cyc <= cyc + 1;
|
||||
if (cyc == 0) begin
|
||||
r_array[0] <= 1;
|
||||
big_array[1][2][3] <= 1;
|
||||
s_array[1].x <= 1;
|
||||
my_struct.x <= 1;
|
||||
end
|
||||
else if (cyc == 1) begin
|
||||
`checkr(r_array[0], 1);
|
||||
`checkr(big_array[1][2][3], 1);
|
||||
`checkh(s_array[1].x, 1);
|
||||
`checkh(my_struct.x, 1);
|
||||
end
|
||||
else if (cyc == 2) begin
|
||||
force r_array[0] = 0;
|
||||
force big_array[1][2][3] = 0;
|
||||
force s_array[1].x = 0;
|
||||
force my_struct.x = 0;
|
||||
end
|
||||
else if (cyc == 3) begin
|
||||
`checkr(r_array[0], 0);
|
||||
`checkr(big_array[1][2][3], 0);
|
||||
r_array[0] <= 1;
|
||||
big_array[1][2][3] <= 1;
|
||||
`checkh(s_array[1].x, 0);
|
||||
s_array[1].x <= 1;
|
||||
`checkh(my_struct.x, 0);
|
||||
my_struct.x <= 1;
|
||||
end
|
||||
else if (cyc == 4) begin
|
||||
`checkr(r_array[0], 0);
|
||||
`checkr(big_array[1][2][3], 0);
|
||||
`checkh(s_array[1].x, 0);
|
||||
`checkh(my_struct.x, 0);
|
||||
end
|
||||
else if (cyc == 5) begin
|
||||
release r_array[0];
|
||||
release big_array[1][2][3];
|
||||
release s_array[1].x;
|
||||
release my_struct.x;
|
||||
end
|
||||
else if (cyc == 6) begin
|
||||
`checkr(r_array[0], 0);
|
||||
`checkr(big_array[1][2][3], 0);
|
||||
r_array[0] <= 1;
|
||||
big_array[1][2][3] <= 1;
|
||||
`checkh(s_array[1].x, 0);
|
||||
s_array[1].x <= 1;
|
||||
`checkh(my_struct.x, 0);
|
||||
my_struct.x <= 1;
|
||||
end
|
||||
else if (cyc == 7) begin
|
||||
`checkr(r_array[0], 1);
|
||||
`checkr(big_array[1][2][3], 1);
|
||||
`checkh(s_array[1].x, 1);
|
||||
`checkh(my_struct.x, 1);
|
||||
end
|
||||
else if (cyc == 8) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
Loading…
Reference in New Issue