Support appending to queue via [] (#5421)

This commit is contained in:
Krzysztof Bieganski 2024-09-02 15:45:47 +02:00 committed by GitHub
parent e9f758ce67
commit 088862d449
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 190 additions and 47 deletions

View File

@ -2206,84 +2206,94 @@ static inline void VL_UNPACK_II(int lbits, int rbits, VlQueue<CData>& q, IData f
const size_t size = (rbits + lbits - 1) / lbits;
q.renew(size);
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = (from >> (i * lbits)) & mask;
for (size_t i = 0; i < size; ++i) q.atWrite(i) = (from >> (i * lbits)) & mask;
}
static inline void VL_UNPACK_II(int lbits, int rbits, VlQueue<SData>& q, IData from) {
const size_t size = (rbits + lbits - 1) / lbits;
q.renew(size);
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = (from >> (i * lbits)) & mask;
for (size_t i = 0; i < size; ++i) q.atWrite(i) = (from >> (i * lbits)) & mask;
}
static inline void VL_UNPACK_II(int lbits, int rbits, VlQueue<IData>& q, IData from) {
const size_t size = (rbits + lbits - 1) / lbits;
q.renew(size);
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = (from >> (i * lbits)) & mask;
for (size_t i = 0; i < size; ++i) q.atWrite(i) = (from >> (i * lbits)) & mask;
}
static inline void VL_UNPACK_IQ(int lbits, int rbits, VlQueue<CData>& q, QData from) {
const size_t size = (rbits + lbits - 1) / lbits;
q.renew(size);
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = (from >> (i * lbits)) & mask;
for (size_t i = 0; i < size; ++i) q.atWrite(i) = (from >> (i * lbits)) & mask;
}
static inline void VL_UNPACK_IQ(int lbits, int rbits, VlQueue<SData>& q, QData from) {
const size_t size = (rbits + lbits - 1) / lbits;
q.renew(size);
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = (from >> (i * lbits)) & mask;
for (size_t i = 0; i < size; ++i) q.atWrite(i) = (from >> (i * lbits)) & mask;
}
static inline void VL_UNPACK_IQ(int lbits, int rbits, VlQueue<IData>& q, QData from) {
const size_t size = (rbits + lbits - 1) / lbits;
q.renew(size);
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = (from >> (i * lbits)) & mask;
for (size_t i = 0; i < size; ++i) q.atWrite(i) = (from >> (i * lbits)) & mask;
}
static inline void VL_UNPACK_QQ(int lbits, int rbits, VlQueue<QData>& q, QData from) {
const size_t size = (rbits + lbits - 1) / lbits;
q.renew(size);
const QData mask = VL_MASK_Q(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = (from >> (i * lbits)) & mask;
for (size_t i = 0; i < size; ++i) q.atWrite(i) = (from >> (i * lbits)) & mask;
}
static inline void VL_UNPACK_IW(int lbits, int rbits, VlQueue<CData>& q, WDataInP rwp) {
const int size = (rbits + lbits - 1) / lbits;
q.renew(size);
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask;
for (size_t i = 0; i < size; ++i) {
q.atWrite(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask;
}
}
static inline void VL_UNPACK_IW(int lbits, int rbits, VlQueue<SData>& q, WDataInP rwp) {
const int size = (rbits + lbits - 1) / lbits;
q.renew(size);
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask;
for (size_t i = 0; i < size; ++i) {
q.atWrite(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask;
}
}
static inline void VL_UNPACK_IW(int lbits, int rbits, VlQueue<IData>& q, WDataInP rwp) {
const int size = (rbits + lbits - 1) / lbits;
q.renew(size);
const IData mask = VL_MASK_I(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask;
for (size_t i = 0; i < size; ++i) {
q.atWrite(i) = VL_SEL_IWII(rbits, rwp, i * lbits, lbits) & mask;
}
}
static inline void VL_UNPACK_QW(int lbits, int rbits, VlQueue<QData>& q, WDataInP rwp) {
const int size = (rbits + lbits - 1) / lbits;
q.renew(size);
const QData mask = VL_MASK_Q(lbits);
for (size_t i = 0; i < size; ++i) q.at(i) = VL_SEL_QWII(rbits, rwp, i * lbits, lbits) & mask;
for (size_t i = 0; i < size; ++i) {
q.atWrite(i) = VL_SEL_QWII(rbits, rwp, i * lbits, lbits) & mask;
}
}
template <std::size_t N>
static inline void VL_UNPACK_WW(int lbits, int rbits, VlQueue<VlWide<N>>& q, WDataInP rwp) {
const int size = (rbits + lbits - 1) / lbits;
q.renew(size);
for (size_t i = 0; i < size; ++i) VL_SEL_WWII(lbits, rbits, q.at(i), rwp, i * lbits, lbits);
for (size_t i = 0; i < size; ++i) {
VL_SEL_WWII(lbits, rbits, q.atWrite(i), rwp, i * lbits, lbits);
}
}
template <std::size_t T_Depth>

View File

@ -593,19 +593,28 @@ public:
return v;
}
// Setting. Verilog: assoc[index] = v
// Can't just overload operator[] or provide a "at" reference to set,
// because we need to be able to insert only when the value is set
T_Value& at(int32_t index) {
// Setting. Verilog: assoc[index] = v (should only be used by dynamic arrays)
T_Value& atWrite(int32_t index) {
// cppcheck-suppress variableScope
static thread_local T_Value t_throwAway;
// Needs to work for dynamic arrays, so does not use T_MaxSize
if (VL_UNLIKELY(index < 0 || index >= m_deque.size())) {
t_throwAway = atDefault();
return t_throwAway;
} else {
return m_deque[index];
}
return m_deque[index];
}
// Setting. Verilog: assoc[index] = v (should only be used by queues)
T_Value& atWriteAppend(int32_t index) {
// cppcheck-suppress variableScope
static thread_local T_Value t_throwAway;
if (VL_UNLIKELY(index < 0 || index > m_deque.size())) {
t_throwAway = atDefault();
return t_throwAway;
} else if (VL_UNLIKELY(index == m_deque.size())) {
push_back(atDefault());
}
return m_deque[index];
}
// Accessing. Verilog: v = assoc[index]
const T_Value& at(int32_t index) const {
@ -617,7 +626,7 @@ public:
}
}
// Access with an index counted from end (e.g. q[$])
T_Value& atBack(int32_t index) { return at(m_deque.size() - 1 - index); }
T_Value& atWriteAppendBack(int32_t index) { return atWriteAppend(m_deque.size() - 1 - index); }
const T_Value& atBack(int32_t index) const { return at(m_deque.size() - 1 - index); }
// function void q.insert(index, value);

View File

@ -2793,6 +2793,7 @@ void AstCMethodHard::setPurity() {
{"assign", false},
{"at", true},
{"atBack", true},
{"atWrite", true},
{"awaitingCurrentTime", true},
{"clear", false},
{"clearFired", false},
@ -2856,6 +2857,16 @@ void AstCMethodHard::setPurity() {
{"word", true},
{"write_var", false}};
if (name() == "atWriteAppend" || name() == "atWriteAppendBack") {
m_pure = false;
// Treat atWriteAppend as pure if the argument is a loop iterator
if (AstNodeExpr* const argp = pinsp()) {
if (AstVarRef* const varrefp = VN_CAST(argp, VarRef)) {
if (varrefp->varp()->isUsedLoopIdx()) m_pure = true;
}
}
return;
}
auto isPureIt = isPureMethod.find(name());
UASSERT_OBJ(isPureIt != isPureMethod.end(), this, "Unknown purity of method " + name());
m_pure = isPureIt->second;

View File

@ -224,7 +224,7 @@ class RandomizeMarkVisitor final : public VNVisitor {
nodep->v3error("Cannot call 'rand_mode()' on packed array element");
valid = false;
} else if (AstCMethodHard* const methodHardp = VN_CAST(fromp, CMethodHard)) {
if (methodHardp->name() == "at" && VN_IS(fromp, CMethodHard)) {
if (methodHardp->name() == "at" || methodHardp->name() == "atWrite") {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported: 'rand_mode()' on dynamic array element");
valid = false;
@ -1194,15 +1194,15 @@ class RandomizeVisitor final : public VNVisitor {
iterVarp->lifetime(VLifetime::AUTOMATIC);
AstCMethodHard* const sizep = new AstCMethodHard{fl, lhsp, "size", nullptr};
sizep->dtypeSetUInt32();
AstCMethodHard* const atp = new AstCMethodHard{fl, lhsp->cloneTree(false), "at",
new AstVarRef{fl, iterVarp, VAccess::READ}};
atp->dtypeSetUInt32();
AstCMethodHard* const setp = new AstCMethodHard{
fl, lhsp->cloneTree(false), "atWrite", new AstVarRef{fl, iterVarp, VAccess::READ}};
setp->dtypeSetUInt32();
AstNode* const stmtsp = iterVarp;
stmtsp->addNext(
new AstAssign{fl, new AstVarRef{fl, iterVarp, VAccess::WRITE}, new AstConst{fl, 0}});
stmtsp->addNext(
new AstWhile{fl, new AstLt{fl, new AstVarRef{fl, iterVarp, VAccess::READ}, sizep},
new AstAssign{fl, atp, rhsp},
new AstAssign{fl, setp, rhsp},
new AstAssign{fl, new AstVarRef{fl, iterVarp, VAccess::WRITE},
new AstAdd{fl, new AstConst{fl, 1},
new AstVarRef{fl, iterVarp, VAccess::READ}}}});
@ -1503,13 +1503,13 @@ class RandomizeVisitor final : public VNVisitor {
const RandomizeMode randMode = {.asInt = memberVarp->user1()};
if (randMode.usesMode
&& !memberVarp->rand().isRand()) { // Not randomizable by default
AstCMethodHard* atp = new AstCMethodHard{
AstCMethodHard* setp = new AstCMethodHard{
nodep->fileline(),
new AstVarRef{fl, VN_AS(randModeVarp->user2p(), NodeModule), randModeVarp,
VAccess::WRITE},
"at", new AstConst{nodep->fileline(), randMode.index}};
atp->dtypeSetUInt32();
newp->addStmtsp(new AstAssign{fl, atp, new AstConst{fl, 0}});
"atWrite", new AstConst{nodep->fileline(), randMode.index}};
setp->dtypeSetUInt32();
newp->addStmtsp(new AstAssign{fl, setp, new AstConst{fl, 0}});
}
if (memberVarp->user3()) return; // Handled in constraints
const AstNodeDType* const dtypep = memberVarp->dtypep()->skipRefp();
@ -1574,10 +1574,10 @@ class RandomizeVisitor final : public VNVisitor {
// mode
const RandomizeMode rmode = {.asInt = receiverp->user1()};
UASSERT_OBJ(rmode.usesMode, ftaskRefp, "Failed to set usesMode");
AstCMethodHard* const atp
= new AstCMethodHard{fl, lhsp, "at", new AstConst{fl, rmode.index}};
atp->dtypeSetUInt32();
m_stmtp->replaceWith(new AstAssign{fl, atp, rhsp});
AstCMethodHard* const setp
= new AstCMethodHard{fl, lhsp, "atWrite", new AstConst{fl, rmode.index}};
setp->dtypeSetUInt32();
m_stmtp->replaceWith(new AstAssign{fl, setp, rhsp});
} else {
// For rand_mode: Called on 'this' or a non-rand class instance.
// For constraint_mode: Called on a class instance.
@ -1589,10 +1589,10 @@ class RandomizeVisitor final : public VNVisitor {
UASSERT_OBJ(receiverp, ftaskRefp, "Should have receiver");
const RandomizeMode rmode = {.asInt = receiverp->user1()};
UASSERT_OBJ(rmode.usesMode, ftaskRefp, "Failed to set usesMode");
AstCMethodHard* const atp
= new AstCMethodHard{fl, lhsp, "at", new AstConst{fl, rmode.index}};
atp->dtypeSetUInt32();
ftaskRefp->replaceWith(atp);
AstCMethodHard* const setp
= new AstCMethodHard{fl, lhsp, "atWrite", new AstConst{fl, rmode.index}};
setp->dtypeSetUInt32();
ftaskRefp->replaceWith(setp);
VL_DO_DANGLING(pushDeletep(ftaskRefp), ftaskRefp);
}
};
@ -1658,12 +1658,12 @@ class RandomizeVisitor final : public VNVisitor {
tmpVarps = AstNode::addNext(tmpVarps, randModeTmpVarp);
}
const RandomizeMode randMode = {.asInt = randVarp->user1()};
AstCMethodHard* atp
AstCMethodHard* setp
= new AstCMethodHard{fl, makeSiblingRefp(exprp, randModeVarp, VAccess::WRITE),
"at", new AstConst{fl, randMode.index}};
atp->dtypeSetUInt32();
"atWrite", new AstConst{fl, randMode.index}};
setp->dtypeSetUInt32();
setStmtsp
= AstNode::addNext(setStmtsp, new AstAssign{fl, atp, new AstConst{fl, 1}});
= AstNode::addNext(setStmtsp, new AstAssign{fl, setp, new AstConst{fl, 1}});
exprp = getFromp(exprp);
}
pinp->unlinkFrBack()->deleteTree();

View File

@ -492,8 +492,12 @@ class TaskVisitor final : public VNVisitor {
if (VN_IS(pinp, VarRef) || VN_IS(pinp, MemberSel) || VN_IS(pinp, StructSel)
|| VN_IS(pinp, ArraySel)) {
refArgOk = true;
} else if (const AstCMethodHard* const cMethodp = VN_CAST(pinp, CMethodHard)) {
} else if (AstCMethodHard* const cMethodp = VN_CAST(pinp, CMethodHard)) {
refArgOk = cMethodp->name() == "at" || cMethodp->name() == "atBack";
if (VN_IS(cMethodp->fromp()->dtypep()->skipRefp(), QueueDType)) {
cMethodp->name(cMethodp->name() == "at" ? "atWriteAppend"
: "atWriteAppendBack");
}
}
if (refArgOk) {
if (AstVarRef* const varrefp = VN_CAST(pinp, VarRef)) {

View File

@ -3557,7 +3557,8 @@ class WidthVisitor final : public VNVisitor {
}
void methodCallLValueRecurse(AstMethodCall* nodep, AstNode* childp, const VAccess& access) {
if (const AstCMethodHard* const ichildp = VN_CAST(childp, CMethodHard)) {
if (ichildp->name() == "at") {
if (ichildp->name() == "at" || ichildp->name() == "atWrite"
|| ichildp->name() == "atWriteAppend" || ichildp->name() == "atWriteAppendBack") {
methodCallLValueRecurse(nodep, ichildp->fromp(), access);
return;
}
@ -3638,9 +3639,14 @@ class WidthVisitor final : public VNVisitor {
AstCMethodHard* newp = nullptr;
if (nodep->name() == "at") { // Created internally for []
methodOkArguments(nodep, 1, 1);
methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "at"};
newp->dtypeFrom(adtypep->subDTypep());
} else if (nodep->name() == "atWrite") { // Created internally for []
methodOkArguments(nodep, 1, 1);
methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
newp
= new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "atWrite"};
newp->dtypeFrom(adtypep->subDTypep());
} else if (nodep->name() == "size") {
methodOkArguments(nodep, 0, 0);
newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(), "size"};
@ -3679,6 +3685,12 @@ class WidthVisitor final : public VNVisitor {
AstCMethodHard* newp = nullptr;
if (nodep->name() == "at" || nodep->name() == "atBack") { // Created internally for []
methodOkArguments(nodep, 1, 1);
newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
nodep->name()};
newp->dtypeFrom(adtypep->subDTypep());
} else if (nodep->name() == "atWriteAppend"
|| nodep->name() == "atWriteAppendBack") { // Created internally for []
methodOkArguments(nodep, 1, 1);
methodCallLValueRecurse(nodep, nodep->fromp(), VAccess::WRITE);
newp = new AstCMethodHard{nodep->fileline(), nodep->fromp()->unlinkFrBack(),
nodep->name()};

View File

@ -215,6 +215,24 @@ class WidthSelVisitor final : public VNVisitor {
}
}
static bool isPossibleWrite(AstNodeExpr* nodep) {
AstNode* abovep = nodep->firstAbovep();
if (AstNodeAssign* const assignp = VN_CAST(abovep, NodeAssign)) {
// On an assign LHS, assume a write
return assignp->lhsp() == nodep;
}
if (AstMethodCall* const methodCallp = VN_CAST(abovep, MethodCall)) {
// A method call can write
return methodCallp->fromp() == nodep;
}
if (AstNodePreSel* const preSelp = VN_CAST(abovep, NodePreSel)) {
// If we're not selected from, it's not a write (we're the index)
if (preSelp->fromp() != nodep) return false;
}
AstNodeExpr* exprp = VN_CAST(abovep, NodeExpr);
return exprp ? isPossibleWrite(exprp) : false;
}
// VISITORS
// If adding new visitors, ensure V3Width's visit(TYPE) calls into here
@ -282,7 +300,9 @@ class WidthSelVisitor final : public VNVisitor {
VL_DO_DANGLING(pushDeletep(nodep), nodep);
} else if (const AstDynArrayDType* const adtypep = VN_CAST(ddtypep, DynArrayDType)) {
// SELBIT(array, index) -> CMETHODCALL(queue, "at", index)
AstCMethodHard* const newp = new AstCMethodHard{nodep->fileline(), fromp, "at", rhsp};
const char* methodName = isPossibleWrite(nodep) ? "atWrite" : "at";
AstCMethodHard* const newp
= new AstCMethodHard{nodep->fileline(), fromp, methodName, rhsp};
newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off queue reference
if (debug() >= 9) newp->dumpTree("- SELBTq: ");
nodep->replaceWith(newp);
@ -290,10 +310,12 @@ class WidthSelVisitor final : public VNVisitor {
} else if (const AstQueueDType* const adtypep = VN_CAST(ddtypep, QueueDType)) {
// SELBIT(array, index) -> CMETHODCALL(queue, "at", index)
AstCMethodHard* newp;
const char* methodName = isPossibleWrite(nodep) ? "atWriteAppend" : "at";
if (AstNodeExpr* const backnessp = selQueueBackness(rhsp)) {
newp = new AstCMethodHard{nodep->fileline(), fromp, "atBack", backnessp};
newp = new AstCMethodHard{nodep->fileline(), fromp,
std::string(methodName) + "Back", backnessp};
} else {
newp = new AstCMethodHard{nodep->fileline(), fromp, "at", rhsp};
newp = new AstCMethodHard{nodep->fileline(), fromp, methodName, rhsp};
}
newp->dtypeFrom(adtypep->subDTypep()); // Need to strip off queue reference
if (debug() >= 9) newp->dumpTree("- SELBTq: ");

View File

@ -196,6 +196,14 @@ module t (/*AUTOARG*/);
`checkh(qa1[0].size, 4);
qa1[0].delete;
qa1[$-1].delete;
`checkh(qa1[$-1].size, 0);
qa1.delete;
`checkh(qa1.size, 0);
`checkh(qa1[$-1].size, 0);
`checkh(qa1.size, 0);
s1.a = new [4];
`checkh(s1.a.size, 4);
s1.a.delete;

View File

@ -22,9 +22,24 @@ module t (/*AUTOARG*/
typedef integer q_t[$];
function void set_val(ref integer lhs, input integer rhs);
lhs = rhs;
endfunction
initial begin
q_t iq;
iq.push_back(42);
// Resize via []
set_val(iq[0], 9000);
`checkh(iq.size(), 1);
`checks(iq[0], 9000);
iq[1]++;
`checkh(iq.size(), 2);
`checks(iq[1], 1);
iq[1000] = 1000;
`checkh(iq.size(), 2);
`checks(iq[1000], 0);
end
always @ (posedge clk) begin
@ -184,6 +199,29 @@ module t (/*AUTOARG*/
`checks(q[0], "front");
//Unsup: `checks(q[$], "front");
// Resize via []
q[0] = "long";
`checkh(q.size(), 1);
`checks(q[0], "long");
end
// Append to queue of queues using []
begin
int q[$][$];
q[0][0] = 1;
`checkh(q.size(), 1);
`checkh(q[0].size(), 1);
`checks(q[0][0], 1);
end
// Do not append with [] if used as index
begin
int p[$];
int q[$];
q[p[0]] = 1;
`checkh(p.size(), 0);
`checkh(q.size(), 1);
`checks(q[0], 1);
end
begin

View File

@ -14,8 +14,9 @@ module t(/*AUTOARG*/);
endfunction
initial begin
q = { 20, 50, 40 };
q = { 60, 50, 40 };
set_val(q[$-1], 30);
q[$-2] = 20;
r = q[$];
if (r != 40) $stop;

View File

@ -19,6 +19,8 @@ module t (/*AUTOARG*/);
if (q.size() != 3) $stop;
q.push_front(0);
if (q.size() != 3) $stop;
q[3] = -1;
if (q.size() != 3) $stop;
if (q[0] != 0) $stop;
if (q[1] != 1) $stop;
if (q[2] != 2) $stop;

View File

@ -11,10 +11,15 @@ module t (/*AUTOARG*/);
task push_data(int val);
que.push_back(val);
endtask
function logic ok;
return '1;
endfunction
endclass
initial begin
Cls c2 [1:0];
Cls cq[$];
c2[0] = new();
@ -25,6 +30,13 @@ module t (/*AUTOARG*/);
c2[0].que.push_back(10); // Unsupported
if (c2[0].que.size() != 2) $stop;
// Test there's no side effect warning on iteration
foreach (cq[i])
case (cq[i].ok())
'0: $stop;
'1: $stop;
endcase
$write("*-* All Finished *-*\n");
$finish;
end

View File

@ -13,6 +13,10 @@ module t (/*AUTOARG*/);
int b[$];
} st_t;
typedef struct {
int v;
} st_in_t;
function automatic st_t bar();
// verilator no_inline_task
for (int i = 0; i < 4; ++i) begin
@ -21,6 +25,7 @@ module t (/*AUTOARG*/);
endfunction // bar
st_t res;
st_in_t q[$];
initial begin
res = bar();
@ -29,6 +34,10 @@ module t (/*AUTOARG*/);
`checkd(res.b[2], 2);
`checkd(res.b[3], 3);
q.push_back(st_in_t'{15});
q[0].v++;
`checkd(q[0].v, 16);
$write("*-* All Finished *-*\n");
$finish;
end

View File

@ -36,6 +36,11 @@ module t (/*AUTOARG*/);
`checks(b0[1], "world");
`checks(b1[0], "bye");
`checks(b1[1], "world");
iq[2][0] = "goodbye";
iq[2][1] = "world";
`checks(iq[2][0], "goodbye");
`checks(iq[2][1], "world");
end
`ifndef verilator