Optimize wide decoder case statements into decoder expressions
Extend the decoder-pattern case optimization to selectors that are too
wide for a full 2^width lookup table. A decoder-pattern case (where
every case item assigns constants to a fixed set of LHSs) is lowered to
a new AstDecoder expression. AstDecoder is emitted as a run-time
VL_DECODER_* function call. It contains two packed constant pool tables,
'matchp' and 'valuep', where 'matchp' is a list of '(mask, bits)' pairs,
and 'valuep' is the corresponding result. This single (albeit
complicated) expression can replace large if-else trees whole, resulting
in much more compact code with fewer static hard to predict branches. It
is worth about 10% speed and 30% code size in some designs.
Example:
```systemverilog
logic [39:0] sel;
always_comb
casez (sel)
40'b???????????????????????????????????????1: out = 8'h01;
40'b??????????????????????????????????????1?: out = 8'h02;
40'b?????????????????????????????????????1??: out = 8'h03;
default: out = 8'hff;
endcase
```
is compiled to:
```c++
out = VL_DECODER_IQ(sel, CONST_match, CONST_value);
```
Where 'CONST_match' contains 4 entries, of a 40-bit mask and 40-bit bit
pattern each, and 'CONST_value' contains 4 entries of the corresponding
8-bit results. (Entries are aligned to word boundaries to avoid runtime
bit swizzling)
Constant folding for these will come in a follow up patch.
This commit is contained in:
parent
5712f9b614
commit
9f398c4256
|
|
@ -663,6 +663,10 @@ Summary:
|
|||
|
||||
Alias for all other `-fno-case-*` options.
|
||||
|
||||
.. option:: -fno-case-decoder
|
||||
|
||||
Rarely needed. Disable converting case statements into decoder tables.
|
||||
|
||||
.. option:: -fno-case-table
|
||||
|
||||
Rarely needed. Disable converting case statements into table lookups.
|
||||
|
|
|
|||
|
|
@ -2876,6 +2876,114 @@ void VL_WRITEMEM_N(bool hex, // Hex format, else binary
|
|||
}
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Decoder functions
|
||||
|
||||
// I input
|
||||
IData VL_DECODER_II(IData index, WDataInP matchp, WDataInP valuep) VL_PURE {
|
||||
size_t i = 0;
|
||||
while (true) {
|
||||
const IData mask = matchp[i * 2];
|
||||
const IData bits = matchp[i * 2 + 1];
|
||||
if ((mask & index) == bits) break;
|
||||
++i;
|
||||
}
|
||||
return valuep[i];
|
||||
}
|
||||
QData VL_DECODER_QI(IData index, WDataInP matchp, WDataInP valuep) VL_PURE {
|
||||
size_t i = 0;
|
||||
while (true) {
|
||||
const IData mask = matchp[i * 2];
|
||||
const IData bits = matchp[i * 2 + 1];
|
||||
if ((mask & index) == bits) break;
|
||||
++i;
|
||||
}
|
||||
return VL_SET_QW(valuep + 2 * i);
|
||||
}
|
||||
WDataOutP VL_DECODER_WI(int owords, WDataOutP owp, IData index, WDataInP matchp,
|
||||
WDataInP valuep) VL_MT_SAFE {
|
||||
size_t i = 0;
|
||||
while (true) {
|
||||
const IData mask = matchp[i * 2];
|
||||
const IData bits = matchp[i * 2 + 1];
|
||||
if ((mask & index) == bits) break;
|
||||
++i;
|
||||
}
|
||||
return VL_MEMCPY_W(owp, valuep + owords * i, owords);
|
||||
}
|
||||
|
||||
// Q input
|
||||
IData VL_DECODER_IQ(QData index, WDataInP matchp, WDataInP valuep) VL_PURE {
|
||||
size_t i = 0;
|
||||
while (true) {
|
||||
const QData mask = VL_SET_QW(matchp + i * 4);
|
||||
const QData bits = VL_SET_QW(matchp + i * 4 + 2);
|
||||
if ((mask & index) == bits) break;
|
||||
++i;
|
||||
}
|
||||
return valuep[i];
|
||||
}
|
||||
QData VL_DECODER_QQ(QData index, WDataInP matchp, WDataInP valuep) VL_PURE {
|
||||
size_t i = 0;
|
||||
while (true) {
|
||||
const QData mask = VL_SET_QW(matchp + i * 4);
|
||||
const QData bits = VL_SET_QW(matchp + i * 4 + 2);
|
||||
if ((mask & index) == bits) break;
|
||||
++i;
|
||||
}
|
||||
return VL_SET_QW(valuep + 2 * i);
|
||||
}
|
||||
WDataOutP VL_DECODER_WQ(int owords, WDataOutP owp, QData index, WDataInP matchp,
|
||||
WDataInP valuep) VL_MT_SAFE {
|
||||
size_t i = 0;
|
||||
while (true) {
|
||||
const QData mask = VL_SET_QW(matchp + i * 4);
|
||||
const QData bits = VL_SET_QW(matchp + i * 4 + 2);
|
||||
if ((mask & index) == bits) break;
|
||||
++i;
|
||||
}
|
||||
return VL_MEMCPY_W(owp, valuep + owords * i, owords);
|
||||
}
|
||||
|
||||
// W input
|
||||
IData VL_DECODER_IW(int iwords, WDataInP indexp, WDataInP matchp, WDataInP valuep) VL_PURE {
|
||||
size_t i = 0;
|
||||
while (true) {
|
||||
const WDataInP maskp = matchp + i * iwords * 2;
|
||||
const WDataInP bitsp = matchp + i * iwords * 2 + iwords;
|
||||
EData diff = 0;
|
||||
for (size_t j = 0; j < iwords; ++j) diff |= (maskp[j] & indexp[j]) ^ bitsp[j];
|
||||
if (!diff) break;
|
||||
++i;
|
||||
}
|
||||
return valuep[i];
|
||||
}
|
||||
QData VL_DECODER_QW(int iwords, WDataInP indexp, WDataInP matchp, WDataInP valuep) VL_PURE {
|
||||
size_t i = 0;
|
||||
while (true) {
|
||||
const WDataInP maskp = matchp + i * iwords * 2;
|
||||
const WDataInP bitsp = matchp + i * iwords * 2 + iwords;
|
||||
EData diff = 0;
|
||||
for (size_t j = 0; j < iwords; ++j) diff |= (maskp[j] & indexp[j]) ^ bitsp[j];
|
||||
if (!diff) break;
|
||||
++i;
|
||||
}
|
||||
return VL_SET_QW(valuep + 2 * i);
|
||||
}
|
||||
WDataOutP VL_DECODER_WW(int owords, WDataOutP owp, int iwords, WDataInP indexp, WDataInP matchp,
|
||||
WDataInP valuep) VL_MT_SAFE {
|
||||
size_t i = 0;
|
||||
while (true) {
|
||||
const WDataInP maskp = matchp + i * iwords * 2;
|
||||
const WDataInP bitsp = matchp + i * iwords * 2 + iwords;
|
||||
EData diff = 0;
|
||||
for (size_t j = 0; j < iwords; ++j) diff |= (maskp[j] & indexp[j]) ^ bitsp[j];
|
||||
if (!diff) break;
|
||||
++i;
|
||||
}
|
||||
return VL_MEMCPY_W(owp, valuep + owords * i, owords);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Timescale conversion
|
||||
|
||||
|
|
|
|||
|
|
@ -269,6 +269,21 @@ static inline VlQueue<T> VL_CVT_UNPACK_TO_Q(const VlUnpacked<T, N_Depth>& q) VL_
|
|||
return ret;
|
||||
}
|
||||
|
||||
IData VL_DECODER_II(IData index, WDataInP matchp, WDataInP valuep) VL_PURE;
|
||||
QData VL_DECODER_QI(IData index, WDataInP matchp, WDataInP valuep) VL_PURE;
|
||||
WDataOutP VL_DECODER_WI(int owords, WDataOutP owp, IData index, WDataInP matchp,
|
||||
WDataInP valuep) VL_MT_SAFE;
|
||||
|
||||
IData VL_DECODER_IQ(QData index, WDataInP matchp, WDataInP valuep) VL_PURE;
|
||||
QData VL_DECODER_QQ(QData index, WDataInP matchp, WDataInP valuep) VL_PURE;
|
||||
WDataOutP VL_DECODER_WQ(int owords, WDataOutP owp, QData index, WDataInP matchp,
|
||||
WDataInP valuep) VL_MT_SAFE;
|
||||
|
||||
IData VL_DECODER_IW(int iwords, WDataInP indexp, WDataInP matchp, WDataInP valuep) VL_PURE;
|
||||
QData VL_DECODER_QW(int iwords, WDataInP indexp, WDataInP matchp, WDataInP valuep) VL_PURE;
|
||||
WDataOutP VL_DECODER_WW(int owords, WDataOutP owp, int iwords, WDataInP indexp, WDataInP matchp,
|
||||
WDataInP valuep) VL_MT_SAFE;
|
||||
|
||||
// Return double from lhs (numeric) unsigned
|
||||
double VL_ITOR_D_W(int lbits, WDataInP const lwp) VL_PURE;
|
||||
static inline double VL_ITOR_D_I(int, IData lhs) VL_PURE {
|
||||
|
|
|
|||
|
|
@ -163,6 +163,14 @@ bool AstVar::sameNode(const AstNode* samep) const {
|
|||
return m_name == asamep->m_name && varType() == asamep->varType();
|
||||
}
|
||||
|
||||
AstDecoder::AstDecoder(FileLine* fl, AstNodeExpr* indexp, AstVarScope* matchp,
|
||||
AstVarScope* valuep)
|
||||
: ASTGEN_SUPER_Decoder(fl) {
|
||||
this->indexp(indexp);
|
||||
this->matchp(new AstVarRef{fl, matchp, VAccess::READ});
|
||||
this->valuep(new AstVarRef{fl, valuep, VAccess::READ});
|
||||
}
|
||||
|
||||
AstVarRef::AstVarRef(FileLine* fl, AstVar* varp, const VAccess& access)
|
||||
: ASTGEN_SUPER_VarRef(fl, varp, access) {
|
||||
if (v3Global.assertDTypesResolved()) {
|
||||
|
|
|
|||
|
|
@ -1328,6 +1328,22 @@ public:
|
|||
string emitC() override { return "VL_CVT_UNPACK_TO_Q(%P, %li)"; }
|
||||
bool cleanOut() const override { return true; }
|
||||
};
|
||||
class AstDecoder final : public AstNodeExpr {
|
||||
// This is a non-source construct, created internally to represent
|
||||
// some case statements. It is a '(mask & _) == bits' matching loop
|
||||
// where {mask, bits} pairs are packed into a single array 'matchp',
|
||||
// and the result is the corresponding value from 'valuep' for the
|
||||
// first matching pair. See VL_DECODER_* runtime functions.
|
||||
// @astgen op1 := indexp : AstNodeExpr
|
||||
// @astgen op2 := matchp : AstVarRef
|
||||
// @astgen op3 := valuep : AstVarRef
|
||||
public:
|
||||
inline AstDecoder(FileLine* fl, AstNodeExpr* indexp, AstVarScope* matchp, AstVarScope* valuep);
|
||||
ASTGEN_MEMBERS_AstDecoder;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
};
|
||||
class AstDist final : public AstNodeExpr {
|
||||
// @astgen op1 := exprp : AstNodeExpr
|
||||
// @astgen op2 := itemsp : List[AstDistItem]
|
||||
|
|
|
|||
235
src/V3Case.cpp
235
src/V3Case.cpp
|
|
@ -166,6 +166,7 @@ class CaseVisitor final : public VNVisitor {
|
|||
// STATE
|
||||
// Statistics tracking, as a struct so can be passed to 'const' methods
|
||||
struct Stats final {
|
||||
VDouble0 caseDecoder; // Cases using decoder method
|
||||
VDouble0 caseTableNormal; // Cases using table method with normal table
|
||||
VDouble0 caseTableTiny; // Cases using table method with tiny table
|
||||
VDouble0 caseFast; // Cases using fast bit tree method
|
||||
|
|
@ -540,15 +541,14 @@ class CaseVisitor final : public VNVisitor {
|
|||
lhsRecord.offset = m_caseDecoderEntryWidth;
|
||||
m_caseDecoderEntryWidth += width;
|
||||
}
|
||||
// Also align the whole entry width to a word boundary
|
||||
m_caseDecoderEntryWidth = VL_WORDS_I(m_caseDecoderEntryWidth) * VL_EDATASIZE;
|
||||
// Check the table fits max size
|
||||
if (fitsLimit(m_caseDecoderEntryWidth, CASE_TABLE_MAX_BITS)) {
|
||||
m_caseTableWidth = m_caseDecoderEntryWidth << caseWidth; // Can optimize
|
||||
const size_t alignedEntryWidth = VL_WORDS_I(m_caseDecoderEntryWidth) * VL_EDATASIZE;
|
||||
if (fitsLimit(alignedEntryWidth, CASE_TABLE_MAX_BITS)) {
|
||||
m_caseTableWidth = alignedEntryWidth << caseWidth; // Can optimize
|
||||
return;
|
||||
}
|
||||
|
||||
// Can't optimize - yet ...
|
||||
// Can optimize as AstDecoder, no other info needed
|
||||
}
|
||||
|
||||
// Analyze case statement. Updates 'm_case*' members. Reports warnings.
|
||||
|
|
@ -593,8 +593,8 @@ class CaseVisitor final : public VNVisitor {
|
|||
for (AstCaseItem* cip = nodep->itemsp(); cip; cip = VN_AS(cip->nextp(), CaseItem)) {
|
||||
// Check conditions
|
||||
for (AstNode* condp = cip->condsp(); condp; condp = condp->nextp()) {
|
||||
// Count conditions
|
||||
++m_caseNConditions;
|
||||
// Count conditions that can actually match.
|
||||
if (!neverItem(nodep, VN_AS(condp, NodeExpr))) ++m_caseNConditions;
|
||||
// Mark opaque if non-constant condition
|
||||
if (!VN_IS(condp, Const)) {
|
||||
m_caseOpaque = true;
|
||||
|
|
@ -619,6 +619,50 @@ class CaseVisitor final : public VNVisitor {
|
|||
if (canBeDecoder) analyzeDecoderPattern(nodep);
|
||||
}
|
||||
|
||||
AstNodeStmt* connectDecoderOutputs(AstCase* nodep, AstNodeExpr* exprp,
|
||||
const char* tmpPrefixp) {
|
||||
FileLine* const flp = nodep->fileline();
|
||||
|
||||
// If there is only one LHS, just use the result
|
||||
if (m_caseDecoderRecords.size() == 1) {
|
||||
const LhsRecord& lhsRecord = m_caseDecoderRecords[0];
|
||||
const int width = lhsRecord.lhsp->width();
|
||||
AstNodeExpr* const rhsp
|
||||
= exprp->width() == width ? exprp : new AstSel{flp, exprp, 0, width};
|
||||
AstNodeExpr* const lhsp = lhsRecord.lhsp->cloneTreePure(false);
|
||||
if (lhsRecord.nCaseAssigns) {
|
||||
return new AstAssign{flp, lhsp, rhsp};
|
||||
} else if (lhsRecord.nCaseAssignDlys) {
|
||||
return new AstAssignDly{flp, lhsp, rhsp};
|
||||
} else {
|
||||
nodep->v3fatalSrc("Unknown assignment type");
|
||||
}
|
||||
}
|
||||
|
||||
// There are multiple LHSs, store the lookup result in a temporary
|
||||
const std::string name = tmpPrefixp + std::to_string(m_nTmps++);
|
||||
AstVarScope* const tempVscp = m_scopep->createTemp(name, m_caseDecoderEntryWidth);
|
||||
AstNodeExpr* const tempWritep = new AstVarRef{flp, tempVscp, VAccess::WRITE};
|
||||
AstNodeStmt* const resultp = new AstAssign{flp, tempWritep, exprp};
|
||||
|
||||
// For each LHS, select out the result
|
||||
for (const LhsRecord& lhsRecord : m_caseDecoderRecords) {
|
||||
const int width = lhsRecord.lhsp->width();
|
||||
const int lsb = lhsRecord.offset;
|
||||
AstNodeExpr* const tempReadp = new AstVarRef{flp, tempVscp, VAccess::READ};
|
||||
AstNodeExpr* const rhsp = new AstSel{flp, tempReadp, lsb, width};
|
||||
AstNodeExpr* const lhsp = lhsRecord.lhsp->cloneTreePure(false);
|
||||
if (lhsRecord.nCaseAssigns) {
|
||||
resultp->addNext(new AstAssign{flp, lhsp, rhsp});
|
||||
} else if (lhsRecord.nCaseAssignDlys) {
|
||||
resultp->addNext(new AstAssignDly{flp, lhsp, rhsp});
|
||||
} else {
|
||||
nodep->v3fatalSrc("Unknown assignment type");
|
||||
}
|
||||
}
|
||||
return resultp;
|
||||
}
|
||||
|
||||
AstNodeStmt* convertCaseTable(AstCase* nodep) {
|
||||
// Create the table constant
|
||||
FileLine* const flp = nodep->fileline();
|
||||
|
|
@ -626,7 +670,17 @@ class CaseVisitor final : public VNVisitor {
|
|||
= new AstConst{flp, AstConst::WidthedValue{}, static_cast<int>(m_caseTableWidth), 0};
|
||||
const uint32_t tableEntries = 1U << nodep->exprp()->width();
|
||||
|
||||
// Populate the table
|
||||
const bool isTinyTable = m_caseTableWidth <= CASE_TABLE_TINY_BITS;
|
||||
if (isTinyTable) {
|
||||
++m_stats.caseTableTiny;
|
||||
} else {
|
||||
++m_stats.caseTableNormal;
|
||||
}
|
||||
|
||||
// Populate the table. Align entries to a word boundary to avoid bit swizzling at runtime.
|
||||
const uint32_t entryWidth = isTinyTable
|
||||
? m_caseDecoderEntryWidth
|
||||
: VL_WORDS_I(m_caseDecoderEntryWidth) * VL_EDATASIZE;
|
||||
for (const LhsRecord& lhsRecord : m_caseDecoderRecords) {
|
||||
const int lhsWidth = lhsRecord.lhsp->width();
|
||||
const int lhsOffset = lhsRecord.offset;
|
||||
|
|
@ -635,7 +689,7 @@ class CaseVisitor final : public VNVisitor {
|
|||
if (lhsRecord.preDefaultp) {
|
||||
AstConst* const rhsp = VN_AS(lhsRecord.preDefaultp->rhsp(), Const);
|
||||
for (uint32_t index = 0; index < tableEntries; ++index) {
|
||||
const uint32_t tableOffset = index * m_caseDecoderEntryWidth + lhsOffset;
|
||||
const uint32_t tableOffset = index * entryWidth + lhsOffset;
|
||||
tablep->num().opSelInto(rhsp->num(), tableOffset, lhsWidth);
|
||||
}
|
||||
}
|
||||
|
|
@ -656,7 +710,7 @@ class CaseVisitor final : public VNVisitor {
|
|||
// If default, broadcast it
|
||||
if (cip->isDefault()) {
|
||||
for (uint32_t index = 0; index < tableEntries; ++index) {
|
||||
const uint32_t tableOffset = index * m_caseDecoderEntryWidth + lhsOffset;
|
||||
const uint32_t tableOffset = index * entryWidth + lhsOffset;
|
||||
tablep->num().opSelInto(rhsp->num(), tableOffset, lhsWidth);
|
||||
}
|
||||
continue;
|
||||
|
|
@ -674,7 +728,7 @@ class CaseVisitor final : public VNVisitor {
|
|||
// i.e.: all don't care values masked out
|
||||
for (uint32_t i = inverseMask; true; i = (i - 1) & inverseMask) {
|
||||
const uint32_t index = i | matchBits;
|
||||
const uint32_t tableOffset = index * m_caseDecoderEntryWidth + lhsOffset;
|
||||
const uint32_t tableOffset = index * entryWidth + lhsOffset;
|
||||
tablep->num().opSelInto(rhsp->num(), tableOffset, lhsWidth);
|
||||
if (!i) break;
|
||||
}
|
||||
|
|
@ -684,11 +738,7 @@ class CaseVisitor final : public VNVisitor {
|
|||
|
||||
// Create the table in the constant pool, unless using an inline table
|
||||
AstVarScope* const tableVscp = [&]() -> AstVarScope* {
|
||||
if (m_caseTableWidth <= CASE_TABLE_TINY_BITS) {
|
||||
++m_stats.caseTableTiny;
|
||||
return nullptr;
|
||||
}
|
||||
++m_stats.caseTableNormal;
|
||||
if (isTinyTable) return nullptr;
|
||||
AstVarScope* vscp = v3Global.rootp()->constPoolp()->findConst(tablep, true);
|
||||
VL_DO_DANGLING(tablep->deleteTree(), tablep); // findConst clones
|
||||
return vscp;
|
||||
|
|
@ -700,49 +750,113 @@ class CaseVisitor final : public VNVisitor {
|
|||
: static_cast<AstNodeExpr*>(tablep);
|
||||
AstNodeExpr* const caseExprp
|
||||
= new AstExtend{flp, nodep->exprp()->cloneTreePure(false), 32};
|
||||
AstNodeExpr* const scalep
|
||||
= new AstConst{flp, static_cast<uint32_t>(m_caseDecoderEntryWidth)};
|
||||
AstNodeExpr* const scalep = new AstConst{flp, entryWidth};
|
||||
AstNodeExpr* const tableLsbp = new AstMul{flp, scalep, caseExprp};
|
||||
|
||||
// If there is only one LHS, just use the result
|
||||
if (m_caseDecoderRecords.size() == 1) {
|
||||
const LhsRecord& lhsRecord = m_caseDecoderRecords[0];
|
||||
const int width = lhsRecord.lhsp->width();
|
||||
AstNodeExpr* const rhsp = new AstSel{flp, tableRefp, tableLsbp, width};
|
||||
AstNodeExpr* const lhsp = lhsRecord.lhsp->cloneTreePure(false);
|
||||
if (lhsRecord.nCaseAssigns) {
|
||||
return new AstAssign{flp, lhsp, rhsp};
|
||||
} else if (lhsRecord.nCaseAssignDlys) {
|
||||
return new AstAssignDly{flp, lhsp, rhsp};
|
||||
} else {
|
||||
nodep->v3fatalSrc("Unknown assignment type");
|
||||
}
|
||||
}
|
||||
|
||||
// There are multiple LHSs, store the lookup result in a temporary
|
||||
const std::string name = "__VcaseTableOut" + std::to_string(m_nTmps++);
|
||||
AstVarScope* const tempVscp = m_scopep->createTemp(name, m_caseDecoderEntryWidth);
|
||||
AstNodeExpr* const tempWritep = new AstVarRef{flp, tempVscp, VAccess::WRITE};
|
||||
AstNodeExpr* const tableSelp
|
||||
= new AstSel{flp, tableRefp, tableLsbp, static_cast<int>(m_caseDecoderEntryWidth)};
|
||||
AstNodeStmt* const resultp = new AstAssign{flp, tempWritep, tableSelp};
|
||||
|
||||
// For each LHS, select out the result
|
||||
for (const LhsRecord& lhsRecord : m_caseDecoderRecords) {
|
||||
const int width = lhsRecord.lhsp->width();
|
||||
const int lsb = lhsRecord.offset;
|
||||
AstNodeExpr* const tempReadp = new AstVarRef{flp, tempVscp, VAccess::READ};
|
||||
AstNodeExpr* const rhsp = new AstSel{flp, tempReadp, lsb, width};
|
||||
AstNodeExpr* const lhsp = lhsRecord.lhsp->cloneTreePure(false);
|
||||
if (lhsRecord.nCaseAssigns) {
|
||||
resultp->addNext(new AstAssign{flp, lhsp, rhsp});
|
||||
} else if (lhsRecord.nCaseAssignDlys) {
|
||||
resultp->addNext(new AstAssignDly{flp, lhsp, rhsp});
|
||||
} else {
|
||||
nodep->v3fatalSrc("Unknown assignment type");
|
||||
// Connect outputs
|
||||
return connectDecoderOutputs(nodep, tableSelp, "__VcaseTableOut");
|
||||
}
|
||||
|
||||
AstNodeStmt* convertCaseDecoder(AstCase* nodep) {
|
||||
++m_stats.caseDecoder;
|
||||
|
||||
FileLine* const flp = nodep->fileline();
|
||||
|
||||
// Gather all the case conditions, paird with their statements. A 'nullptr' condition
|
||||
// matches anything (the default case, or the catch-all added below).
|
||||
std::vector<std::pair<AstConst*, AstNode*>> clauses; // (condition, item statements)
|
||||
clauses.reserve(m_caseNConditions + 1);
|
||||
for (AstCaseItem* cip = nodep->itemsp(); cip; cip = VN_AS(cip->nextp(), CaseItem)) {
|
||||
if (cip->isDefault()) {
|
||||
clauses.emplace_back(nullptr, cip->stmtsp());
|
||||
continue;
|
||||
}
|
||||
for (AstNode* condp = cip->condsp(); condp; condp = condp->nextp()) {
|
||||
AstConst* const iconstp = VN_AS(condp, Const);
|
||||
// Skip items that can never match in 2-state simulation (e.g. X in casez)
|
||||
if (neverItem(nodep, iconstp)) continue;
|
||||
clauses.emplace_back(iconstp, cip->stmtsp());
|
||||
}
|
||||
}
|
||||
return resultp;
|
||||
// If the case has no default item and is not provably exhaustive, unmatched selector
|
||||
// values fall back to the pre-defaults. Represent that with a catch-all clause (null
|
||||
// condition and no statements, so every LHS uses its pre-default). 'analyzeDecoderPattern'
|
||||
// guarantees every LHS has a pre-default in this case.
|
||||
const bool provenExhaustive = m_caseDetailsValid && m_caseDetails.exhaustive
|
||||
&& !m_caseDetails.exhaustiveOverEnumOnly;
|
||||
if (clauses.back().first && !provenExhaustive) clauses.emplace_back(nullptr, nullptr);
|
||||
|
||||
// Number of entries in decoder table
|
||||
const int decoderEnries = clauses.size();
|
||||
|
||||
// Build the match table: a {matchBits, matchMask} packed pair per clause, shared by all
|
||||
// LHSs. Each field is rounded up to a whole EDATA word boundary to avoid bit swizzling
|
||||
// at runtime. We use a packed value so the runtime function can take a VlWide pointer
|
||||
// without templating on the array size.
|
||||
const int condWidth = nodep->exprp()->width();
|
||||
const int matchWidth = 2 * VL_WORDS_I(condWidth) * VL_EDATASIZE;
|
||||
AstConst* const matchp
|
||||
= new AstConst{flp, AstConst::WidthedValue{}, decoderEnries * matchWidth, 0};
|
||||
for (int i = 0; i < decoderEnries; ++i) {
|
||||
const int entryLsb = i * matchWidth;
|
||||
|
||||
// If the entry has a condition, use it's match bits and mask
|
||||
if (AstConst* const condp = clauses[i].first) {
|
||||
const auto& match = matchPattern(nodep, condp);
|
||||
matchp->num().opSelInto(match.first, entryLsb, condWidth);
|
||||
matchp->num().opSelInto(match.second, entryLsb + matchWidth / 2, condWidth);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise use zero for mask and bits, which matches anything
|
||||
V3Number numZero{flp, condWidth, 0};
|
||||
matchp->num().opSelInto(numZero, entryLsb, condWidth);
|
||||
matchp->num().opSelInto(numZero, entryLsb + matchWidth / 2, condWidth);
|
||||
}
|
||||
|
||||
// Build a single value table for all LHSs: one entry per clause, packing each LHS's value
|
||||
// at its offset. The entry width is the table packing computed by 'analyzeDecoderPattern'
|
||||
// Rounded up to a whole EDATA word boundary to avoid bit swizzling at runtime.
|
||||
const int valueWidth = VL_WORDS_I(m_caseDecoderEntryWidth) * VL_EDATASIZE;
|
||||
AstConst* const valuep
|
||||
= new AstConst{flp, AstConst::WidthedValue{}, decoderEnries * valueWidth, 0};
|
||||
for (int i = 0; i < decoderEnries; ++i) {
|
||||
AstNode* const stmtsp = clauses[i].second;
|
||||
for (const LhsRecord& lhsRecord : m_caseDecoderRecords) {
|
||||
AstNodeExpr* const lhsp = lhsRecord.lhsp;
|
||||
// Find the value assigned to this LHS in the clause's statements
|
||||
AstConst* valConstp = nullptr;
|
||||
for (AstNode* stmtp = stmtsp; stmtp; stmtp = stmtp->nextp()) {
|
||||
AstNodeAssign* const assignp = VN_AS(stmtp, NodeAssign);
|
||||
if (!lhsp->sameTree(assignp->lhsp())) continue;
|
||||
valConstp = VN_AS(assignp->rhsp(), Const);
|
||||
break;
|
||||
}
|
||||
// Not assigned in this clause, so use the pre-assigned default
|
||||
if (!valConstp) {
|
||||
UASSERT_OBJ(lhsRecord.preDefaultp, nodep,
|
||||
"Decoder LHS unassigned in case item without a pre-default");
|
||||
valConstp = VN_AS(lhsRecord.preDefaultp->rhsp(), Const);
|
||||
}
|
||||
valuep->num().opSelInto(valConstp->num(), i * valueWidth + lhsRecord.offset,
|
||||
lhsp->width());
|
||||
}
|
||||
}
|
||||
|
||||
// Create the tables
|
||||
AstVarScope* const matchVscp = v3Global.rootp()->constPoolp()->findConst(matchp, true);
|
||||
AstVarScope* const valueVscp = v3Global.rootp()->constPoolp()->findConst(valuep, true);
|
||||
VL_DO_DANGLING(matchp->deleteTree(), matchp);
|
||||
VL_DO_DANGLING(valuep->deleteTree(), valuep);
|
||||
|
||||
// The decoder produces one packed entry holding every LHS's value
|
||||
AstNodeExpr* const indexp = nodep->exprp()->cloneTreePure(false);
|
||||
AstDecoder* const decoderp = new AstDecoder{flp, indexp, matchVscp, valueVscp};
|
||||
decoderp->dtypeSetLogicSized(m_caseDecoderEntryWidth, VSigning::UNSIGNED);
|
||||
|
||||
return connectDecoderOutputs(nodep, decoderp, "__VcaseDecoderOut");
|
||||
}
|
||||
|
||||
// TODO: should return AstNodeStmt after #6280
|
||||
|
|
@ -965,6 +1079,20 @@ class CaseVisitor final : public VNVisitor {
|
|||
}();
|
||||
if (useTable) return convertCaseTable(nodep);
|
||||
|
||||
// Determine if we should use the decoder method.
|
||||
const bool useDecoder = [&]() {
|
||||
// Not if disabled
|
||||
if (!v3Global.opt.fCaseDecoder()) return false;
|
||||
// Not if not a decoder pattern
|
||||
if (m_caseDecoderRecords.empty()) return false;
|
||||
// Only worth it once the branch lowering it would replace is deep enough (see
|
||||
// useTable)
|
||||
const size_t branches = std::min<size_t>(nodep->exprp()->width(), m_caseNConditions);
|
||||
if (branches < CASE_TABLE_MIN_BRANCHES) return false;
|
||||
return true;
|
||||
}();
|
||||
if (useDecoder) return convertCaseDecoder(nodep);
|
||||
|
||||
// Determine if we should use the fast bitwise branching tree method
|
||||
const bool useFastBitTree = [&]() {
|
||||
// Not if disabled
|
||||
|
|
@ -1044,6 +1172,7 @@ public:
|
|||
// CONSTRUCTORS
|
||||
explicit CaseVisitor(AstNetlist* nodep) { iterate(nodep); }
|
||||
~CaseVisitor() override {
|
||||
V3Stats::addStat("Optimizations, Cases decoder", m_stats.caseDecoder);
|
||||
V3Stats::addStat("Optimizations, Cases table normal", m_stats.caseTableNormal);
|
||||
V3Stats::addStat("Optimizations, Cases table tiny", m_stats.caseTableTiny);
|
||||
V3Stats::addStat("Optimizations, Cases parallelized", m_stats.caseFast);
|
||||
|
|
|
|||
|
|
@ -232,6 +232,11 @@ class CleanVisitor final : public VNVisitor {
|
|||
ensureClean(nodep->rhsp());
|
||||
setClean(nodep, true);
|
||||
}
|
||||
void visit(AstDecoder* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
ensureClean(nodep->indexp());
|
||||
setClean(nodep, true);
|
||||
}
|
||||
void visit(AstSel* nodep) override {
|
||||
operandBiop(nodep);
|
||||
setClean(nodep, nodep->cleanOut());
|
||||
|
|
|
|||
|
|
@ -3019,6 +3019,13 @@ class ConstVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
void visit(AstClassOrPackageRef* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
void visit(AstDecoder* nodep) override {
|
||||
// Do not iterate the tables, they must be constant pool entries
|
||||
iterate(nodep->indexp());
|
||||
// TODO: constant fold
|
||||
}
|
||||
|
||||
void visit(AstPin* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
void replaceLogEq(AstLogEq* nodep) {
|
||||
|
|
|
|||
|
|
@ -96,6 +96,11 @@ std::unique_ptr<DfgGraph> DfgGraph::clone() const {
|
|||
vtxp2clonep.emplace(&vtx, cp);
|
||||
break;
|
||||
} // LCOV_EXCL_STOP
|
||||
case VDfgType::Decoder: {
|
||||
DfgDecoder* const cp = new DfgDecoder{*clonep, vtx.fileline(), vtx.dtype()};
|
||||
vtxp2clonep.emplace(&vtx, cp);
|
||||
break;
|
||||
}
|
||||
case VDfgType::Sel: {
|
||||
DfgSel* const cp = new DfgSel{*clonep, vtx.fileline(), vtx.dtype()};
|
||||
cp->lsb(vtx.as<DfgSel>()->lsb());
|
||||
|
|
@ -666,6 +671,16 @@ void DfgVertex::typeCheck(const DfgGraph& dfg) const {
|
|||
CHECK(v.srcp()->dtype() == v.dtype().elemDtype(), "Input should be the element type");
|
||||
return;
|
||||
}
|
||||
case VDfgType::Decoder: {
|
||||
const DfgDecoder& v = *as<DfgDecoder>();
|
||||
CHECK(v.isPacked(), "Should be Packed type");
|
||||
CHECK(v.indexp()->isPacked(), "Index should be Packed type");
|
||||
CHECK(v.matchp()->isPacked(), "Match should be Packed type");
|
||||
CHECK(v.valuep()->isPacked(), "Value should be Packed type");
|
||||
CHECK(v.matchp()->is<DfgVertexVar>(), "Match should be a variable");
|
||||
CHECK(v.matchp()->is<DfgVertexVar>(), "Value should be a variable");
|
||||
return;
|
||||
}
|
||||
case VDfgType::Sel: {
|
||||
const DfgSel& v = *as<DfgSel>();
|
||||
CHECK(v.isPacked(), "Should be Packed type");
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ class V3DfgCse final {
|
|||
}
|
||||
|
||||
// Vertices with no internal information
|
||||
case VDfgType::Decoder:
|
||||
case VDfgType::Mux:
|
||||
case VDfgType::UnitArray: return V3Hash{};
|
||||
|
||||
|
|
@ -197,6 +198,7 @@ class V3DfgCse final {
|
|||
}
|
||||
|
||||
// Vertices with no internal information
|
||||
case VDfgType::Decoder:
|
||||
case VDfgType::Mux:
|
||||
case VDfgType::UnitArray: return true;
|
||||
|
||||
|
|
|
|||
|
|
@ -234,6 +234,14 @@ class DfgToAstVisitor final : DfgVisitor {
|
|||
AstVar* const varp = sinkp->as<DfgVertexVar>()->vscp()->varp();
|
||||
m_resultp = new AstCReset{vtxp->fileline(), varp, false};
|
||||
}
|
||||
void visit(DfgDecoder* vtxp) override {
|
||||
FileLine* const flp = vtxp->fileline();
|
||||
AstNodeExpr* const indexp = convertDfgVertexToAstNodeExpr(vtxp->indexp());
|
||||
AstVarScope* const matchp = vtxp->matchp()->as<DfgVertexVar>()->vscp();
|
||||
AstVarScope* const valuep = vtxp->valuep()->as<DfgVertexVar>()->vscp();
|
||||
m_resultp = new AstDecoder{flp, indexp, matchp, valuep};
|
||||
m_resultp->dtypeSetLogicSized(vtxp->width(), VSigning::UNSIGNED);
|
||||
}
|
||||
|
||||
void visit(DfgRep* vtxp) override {
|
||||
FileLine* const flp = vtxp->fileline();
|
||||
|
|
|
|||
|
|
@ -402,6 +402,32 @@ class AstToDfgConverter final : public VNVisitor {
|
|||
DfgVertex* const vtxp = make<DfgCReset>(nodep->fileline(), *dtypep);
|
||||
nodep->user2p(vtxp);
|
||||
}
|
||||
void visit(AstDecoder* nodep) override {
|
||||
UASSERT_OBJ(m_converting, nodep, "AstToDfg visit called without m_converting");
|
||||
UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex");
|
||||
if (unhandled(nodep)) return;
|
||||
|
||||
const DfgDataType* const dtypep = DfgDataType::fromAst(nodep->dtypep());
|
||||
if (!dtypep) {
|
||||
m_foundUnhandled = true;
|
||||
++m_ctx.m_conv.nonRepDType;
|
||||
return;
|
||||
}
|
||||
|
||||
iterate(nodep->indexp());
|
||||
if (m_foundUnhandled) return;
|
||||
iterate(nodep->matchp());
|
||||
if (m_foundUnhandled) return;
|
||||
iterate(nodep->valuep());
|
||||
if (m_foundUnhandled) return;
|
||||
|
||||
FileLine* const flp = nodep->fileline();
|
||||
DfgDecoder* const vtxp = make<DfgDecoder>(flp, *dtypep);
|
||||
vtxp->indexp(nodep->indexp()->user2u().to<DfgVertex*>());
|
||||
vtxp->matchp(nodep->matchp()->user2u().to<DfgVertex*>());
|
||||
vtxp->valuep(nodep->valuep()->user2u().to<DfgVertex*>());
|
||||
nodep->user2p(vtxp);
|
||||
}
|
||||
void visit(AstReplicate* nodep) override {
|
||||
UASSERT_OBJ(m_converting, nodep, "AstToDfg visit called without m_converting");
|
||||
UASSERT_OBJ(!nodep->user2p(), nodep, "Already has Dfg vertex");
|
||||
|
|
|
|||
|
|
@ -362,6 +362,24 @@ public:
|
|||
ASTGEN_MEMBERS_DfgVertexTernary;
|
||||
};
|
||||
|
||||
class DfgDecoder final : public DfgVertexTernary {
|
||||
public:
|
||||
DfgDecoder(DfgGraph& dfg, FileLine* flp, const DfgDataType& dtype)
|
||||
: DfgVertexTernary{dfg, dfgType(), flp, dtype} {}
|
||||
ASTGEN_MEMBERS_DfgDecoder;
|
||||
|
||||
DfgVertex* indexp() const { return inputp(0); }
|
||||
void indexp(DfgVertex* vtxp) { inputp(0, vtxp); }
|
||||
DfgVertex* matchp() const { return inputp(1); }
|
||||
void matchp(DfgVertex* vtxp) { inputp(1, vtxp); }
|
||||
DfgVertex* valuep() const { return inputp(2); }
|
||||
void valuep(DfgVertex* vtxp) { inputp(2, vtxp); }
|
||||
|
||||
std::string srcName(size_t idx) const override {
|
||||
return idx == 0 ? "indexp" : idx == 1 ? "matchp" : "valuep";
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Variadic vertices - variable number of inputs
|
||||
|
||||
|
|
|
|||
|
|
@ -1590,6 +1590,28 @@ public:
|
|||
puts(")");
|
||||
}
|
||||
}
|
||||
void visit(AstDecoder* nodep) override {
|
||||
putns(nodep, "VL_DECODER_");
|
||||
emitIQW(nodep);
|
||||
emitIQW(nodep->indexp());
|
||||
puts("(");
|
||||
if (nodep->isWide()) {
|
||||
puts(std::to_string(nodep->widthWords()));
|
||||
puts(", ");
|
||||
iterateConst(m_wideTempRefp);
|
||||
puts(", ");
|
||||
}
|
||||
if (nodep->indexp()->isWide()) {
|
||||
puts(std::to_string(nodep->indexp()->widthWords()));
|
||||
puts(", ");
|
||||
}
|
||||
iterateConst(nodep->indexp());
|
||||
puts(", ");
|
||||
iterateConst(nodep->matchp());
|
||||
puts(", ");
|
||||
iterateConst(nodep->valuep());
|
||||
puts(")");
|
||||
}
|
||||
void visit(AstMemberSel* nodep) override {
|
||||
iterateAndNextConstNull(nodep->fromp());
|
||||
putnbs(nodep, "->");
|
||||
|
|
|
|||
|
|
@ -1449,9 +1449,11 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
|
|||
DECL_OPTION("-facyc-simp", FOnOff, &m_fAcycSimp);
|
||||
DECL_OPTION("-fassemble", FOnOff, &m_fAssemble);
|
||||
DECL_OPTION("-fcase", CbFOnOff, [this](bool flag) {
|
||||
m_fCaseDecoder = flag;
|
||||
m_fCaseTable = flag;
|
||||
m_fCaseTree = flag;
|
||||
});
|
||||
DECL_OPTION("-fcase-decoder", FOnOff, &m_fCaseDecoder);
|
||||
DECL_OPTION("-fcase-table", FOnOff, &m_fCaseTable);
|
||||
DECL_OPTION("-fcase-tree", FOnOff, &m_fCaseTree);
|
||||
DECL_OPTION("-fcombine", FOnOff, &m_fCombine);
|
||||
|
|
@ -2356,6 +2358,7 @@ void V3Options::optimize(int level) {
|
|||
const bool flag = level > 0;
|
||||
m_fAcycSimp = flag;
|
||||
m_fAssemble = flag;
|
||||
m_fCaseDecoder = flag;
|
||||
m_fCaseTable = flag;
|
||||
m_fCaseTree = flag;
|
||||
m_fCombine = flag;
|
||||
|
|
|
|||
|
|
@ -392,6 +392,7 @@ private:
|
|||
// MEMBERS (optimizations)
|
||||
bool m_fAcycSimp; // main switch: -fno-acyc-simp: acyclic pre-optimizations
|
||||
bool m_fAssemble; // main switch: -fno-assemble: assign assemble
|
||||
bool m_fCaseDecoder; // main switch: -fno-case-decoder: case decoder conversion
|
||||
bool m_fCaseTable; // main switch: -fno-case-table: case table conversion
|
||||
bool m_fCaseTree; // main switch: -fno-case-tree: case tree conversion
|
||||
bool m_fCombine; // main switch: -fno-combine: common icode packing
|
||||
|
|
@ -726,6 +727,7 @@ public:
|
|||
// ACCESSORS (optimization options)
|
||||
bool fAcycSimp() const { return m_fAcycSimp; }
|
||||
bool fAssemble() const { return m_fAssemble; }
|
||||
bool fCaseDecoder() const { return m_fCaseDecoder; }
|
||||
bool fCaseTable() const { return m_fCaseTable; }
|
||||
bool fCaseTree() const { return m_fCaseTree; }
|
||||
bool fCombine() const { return m_fCombine; }
|
||||
|
|
|
|||
|
|
@ -347,6 +347,10 @@ class PremitVisitor final : public VNVisitor {
|
|||
}
|
||||
checkNode(nodep);
|
||||
}
|
||||
void visit(AstDecoder* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
checkNode(nodep);
|
||||
}
|
||||
void visit(AstCond* nodep) override {
|
||||
// Convert AstCond to AstIf in order to avoid evaluating
|
||||
// sub-expressions in both branches unconditionally.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
#!/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('vlt')
|
||||
|
||||
test.compile(verilator_flags2=['--binary', '--stats'])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.file_grep(test.stats, r'Optimizations, Cases decoder\s+(\d+)', 16)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,507 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
//
|
||||
// Case statements that become a "decoder" (the selector is matched against a packed constant
|
||||
// table at runtime), followed by cases that must not be converted to one. Each output is
|
||||
// compared against an equivalent reference computed without a case statement, so the reference
|
||||
// itself is never converted.
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
module t;
|
||||
logic clk = 1'b0;
|
||||
always #5 clk = ~clk;
|
||||
|
||||
logic [31:0] cyc = 0;
|
||||
|
||||
// Accept A: a 31-bit (I) selector decoded into outputs of three widths (I/Q/W result).
|
||||
wire [30:0] accept_a_in = 31'b1 << cyc[3:0];
|
||||
logic [ 5:0] accept_a_out_0, accept_a_ref_0;
|
||||
logic [ 55:0] accept_a_out_1, accept_a_ref_1;
|
||||
logic [142:0] accept_a_out_2, accept_a_ref_2;
|
||||
always_comb begin
|
||||
casez (accept_a_in)
|
||||
31'b???????_????????_????????_???????1 : accept_a_out_0 = 6'd00;
|
||||
31'b???????_????????_????????_??????1? : accept_a_out_0 = 6'd01;
|
||||
31'b???????_????????_????????_?????1?? : accept_a_out_0 = 6'd02;
|
||||
31'b???????_????????_????????_????1??? : accept_a_out_0 = 6'd03;
|
||||
31'b???????_????????_????????_???1???? : accept_a_out_0 = 6'd04;
|
||||
31'b???????_????????_????????_??1????? : accept_a_out_0 = 6'd05;
|
||||
31'b???????_????????_????????_?1?????? : accept_a_out_0 = 6'd06;
|
||||
31'b???????_????????_????????_1??????? : accept_a_out_0 = 6'd07;
|
||||
31'b???????_????????_???????1_???????? : accept_a_out_0 = 6'd08;
|
||||
31'b???????_????????_??????1?_???????? : accept_a_out_0 = 6'd09;
|
||||
31'b???????_????????_?????1??_???????? : accept_a_out_0 = 6'd10;
|
||||
31'b???????_????????_????1???_???????? : accept_a_out_0 = 6'd11;
|
||||
31'b???????_????????_???1????_???????? : accept_a_out_0 = 6'd12;
|
||||
31'b???????_????????_??1?????_???????? : accept_a_out_0 = 6'd13;
|
||||
31'b???????_????????_?1??????_???????? : accept_a_out_0 = 6'd14;
|
||||
31'b???????_????????_1???????_???????? : accept_a_out_0 = 6'd15;
|
||||
31'b???????_???????1_????????_???????? : accept_a_out_0 = 6'd16;
|
||||
31'b???????_??????1?_????????_???????? : accept_a_out_0 = 6'd17;
|
||||
31'b???????_?????1??_????????_???????? : accept_a_out_0 = 6'd18;
|
||||
31'b???????_????1???_????????_???????? : accept_a_out_0 = 6'd19;
|
||||
31'b???????_???1????_????????_???????? : accept_a_out_0 = 6'd20;
|
||||
31'b???????_??1?????_????????_???????? : accept_a_out_0 = 6'd21;
|
||||
31'b???????_?1??????_????????_???????? : accept_a_out_0 = 6'd22;
|
||||
31'b???????_1???????_????????_???????? : accept_a_out_0 = 6'd23;
|
||||
31'b??????1_????????_????????_???????? : accept_a_out_0 = 6'd24;
|
||||
31'b?????1?_????????_????????_???????? : accept_a_out_0 = 6'd25;
|
||||
31'b????1??_????????_????????_???????? : accept_a_out_0 = 6'd26;
|
||||
31'b???1???_????????_????????_???????? : accept_a_out_0 = 6'd27;
|
||||
31'b??1????_????????_????????_???????? : accept_a_out_0 = 6'd28;
|
||||
31'b?1?????_????????_????????_???????? : accept_a_out_0 = 6'd29;
|
||||
31'b1??????_????????_????????_???????? : accept_a_out_0 = 6'd30;
|
||||
default: accept_a_out_0 = '1;
|
||||
endcase
|
||||
casez (accept_a_in)
|
||||
31'b???????_????????_????????_???????1 : accept_a_out_1 = 56'd0000;
|
||||
31'b???????_????????_????????_??????1? : accept_a_out_1 = 56'd0100;
|
||||
31'b???????_????????_????????_?????1?? : accept_a_out_1 = 56'd0200;
|
||||
31'b???????_????????_????????_????1??? : accept_a_out_1 = 56'd0300;
|
||||
31'b???????_????????_????????_???1???? : accept_a_out_1 = 56'd0400;
|
||||
31'b???????_????????_????????_??1????? : accept_a_out_1 = 56'd0500;
|
||||
31'b???????_????????_????????_?1?????? : accept_a_out_1 = 56'd0600;
|
||||
31'b???????_????????_????????_1??????? : accept_a_out_1 = 56'd0700;
|
||||
31'b???????_????????_???????1_???????? : accept_a_out_1 = 56'd0800;
|
||||
31'b???????_????????_??????1?_???????? : accept_a_out_1 = 56'd0900;
|
||||
31'b???????_????????_?????1??_???????? : accept_a_out_1 = 56'd1000;
|
||||
31'b???????_????????_????1???_???????? : accept_a_out_1 = 56'd1100;
|
||||
31'b???????_????????_???1????_???????? : accept_a_out_1 = 56'd1200;
|
||||
31'b???????_????????_??1?????_???????? : accept_a_out_1 = 56'd1300;
|
||||
31'b???????_????????_?1??????_???????? : accept_a_out_1 = 56'd1400;
|
||||
31'b???????_????????_1???????_???????? : accept_a_out_1 = 56'd1500;
|
||||
31'b???????_???????1_????????_???????? : accept_a_out_1 = 56'd1600;
|
||||
31'b???????_??????1?_????????_???????? : accept_a_out_1 = 56'd1700;
|
||||
31'b???????_?????1??_????????_???????? : accept_a_out_1 = 56'd1800;
|
||||
31'b???????_????1???_????????_???????? : accept_a_out_1 = 56'd1900;
|
||||
31'b???????_???1????_????????_???????? : accept_a_out_1 = 56'd2000;
|
||||
31'b???????_??1?????_????????_???????? : accept_a_out_1 = 56'd2100;
|
||||
31'b???????_?1??????_????????_???????? : accept_a_out_1 = 56'd2200;
|
||||
31'b???????_1???????_????????_???????? : accept_a_out_1 = 56'd2300;
|
||||
31'b??????1_????????_????????_???????? : accept_a_out_1 = 56'd2400;
|
||||
31'b?????1?_????????_????????_???????? : accept_a_out_1 = 56'd2500;
|
||||
31'b????1??_????????_????????_???????? : accept_a_out_1 = 56'd2600;
|
||||
31'b???1???_????????_????????_???????? : accept_a_out_1 = 56'd2700;
|
||||
31'b??1????_????????_????????_???????? : accept_a_out_1 = 56'd2800;
|
||||
31'b?1?????_????????_????????_???????? : accept_a_out_1 = 56'd2900;
|
||||
31'b1??????_????????_????????_???????? : accept_a_out_1 = 56'd3000;
|
||||
default: accept_a_out_1 = '1;
|
||||
endcase
|
||||
casez (accept_a_in)
|
||||
31'b???????_????????_????????_???????1 : accept_a_out_2 = 143'd0000000000;
|
||||
31'b???????_????????_????????_??????1? : accept_a_out_2 = 143'd0100000000;
|
||||
31'b???????_????????_????????_?????1?? : accept_a_out_2 = 143'd0200000000;
|
||||
31'b???????_????????_????????_????1??? : accept_a_out_2 = 143'd0300000000;
|
||||
31'b???????_????????_????????_???1???? : accept_a_out_2 = 143'd0400000000;
|
||||
31'b???????_????????_????????_??1????? : accept_a_out_2 = 143'd0500000000;
|
||||
31'b???????_????????_????????_?1?????? : accept_a_out_2 = 143'd0600000000;
|
||||
31'b???????_????????_????????_1??????? : accept_a_out_2 = 143'd0700000000;
|
||||
31'b???????_????????_???????1_???????? : accept_a_out_2 = 143'd0800000000;
|
||||
31'b???????_????????_??????1?_???????? : accept_a_out_2 = 143'd0900000000;
|
||||
31'b???????_????????_?????1??_???????? : accept_a_out_2 = 143'd1000000000;
|
||||
31'b???????_????????_????1???_???????? : accept_a_out_2 = 143'd1100000000;
|
||||
31'b???????_????????_???1????_???????? : accept_a_out_2 = 143'd1200000000;
|
||||
31'b???????_????????_??1?????_???????? : accept_a_out_2 = 143'd1300000000;
|
||||
31'b???????_????????_?1??????_???????? : accept_a_out_2 = 143'd1400000000;
|
||||
31'b???????_????????_1???????_???????? : accept_a_out_2 = 143'd1500000000;
|
||||
31'b???????_???????1_????????_???????? : accept_a_out_2 = 143'd1600000000;
|
||||
31'b???????_??????1?_????????_???????? : accept_a_out_2 = 143'd1700000000;
|
||||
31'b???????_?????1??_????????_???????? : accept_a_out_2 = 143'd1800000000;
|
||||
31'b???????_????1???_????????_???????? : accept_a_out_2 = 143'd1900000000;
|
||||
31'b???????_???1????_????????_???????? : accept_a_out_2 = 143'd2000000000;
|
||||
31'b???????_??1?????_????????_???????? : accept_a_out_2 = 143'd2100000000;
|
||||
31'b???????_?1??????_????????_???????? : accept_a_out_2 = 143'd2200000000;
|
||||
31'b???????_1???????_????????_???????? : accept_a_out_2 = 143'd2300000000;
|
||||
31'b??????1_????????_????????_???????? : accept_a_out_2 = 143'd2400000000;
|
||||
31'b?????1?_????????_????????_???????? : accept_a_out_2 = 143'd2500000000;
|
||||
31'b????1??_????????_????????_???????? : accept_a_out_2 = 143'd2600000000;
|
||||
31'b???1???_????????_????????_???????? : accept_a_out_2 = 143'd2700000000;
|
||||
31'b??1????_????????_????????_???????? : accept_a_out_2 = 143'd2800000000;
|
||||
31'b?1?????_????????_????????_???????? : accept_a_out_2 = 143'd2900000000;
|
||||
31'b1??????_????????_????????_???????? : accept_a_out_2 = 143'd3000000000;
|
||||
default: accept_a_out_2 = '1;
|
||||
endcase
|
||||
end
|
||||
assign accept_a_ref_0 = 6'(cyc[3:0]);
|
||||
assign accept_a_ref_1 = 56'(cyc[3:0]) * 56'd100;
|
||||
assign accept_a_ref_2 = 143'(cyc[3:0]) * 143'd100000000;
|
||||
|
||||
// Accept B: a 40-bit (Q) selector decoded into outputs of three widths (I/Q/W result).
|
||||
wire [39:0] accept_b_in = 40'b1 << cyc[5:1];
|
||||
logic [ 5:0] accept_b_out_0, accept_b_ref_0;
|
||||
logic [ 55:0] accept_b_out_1, accept_b_ref_1;
|
||||
logic [142:0] accept_b_out_2, accept_b_ref_2;
|
||||
always_comb begin
|
||||
casez (accept_b_in)
|
||||
40'b????????_????????_????????_????????_???????1 : accept_b_out_0 = 6'd00;
|
||||
40'b????????_????????_????????_????????_??????1? : accept_b_out_0 = 6'd01;
|
||||
40'b????????_????????_????????_????????_?????1?? : accept_b_out_0 = 6'd02;
|
||||
40'b????????_????????_????????_????????_????1??? : accept_b_out_0 = 6'd03;
|
||||
40'b????????_????????_????????_????????_???1???? : accept_b_out_0 = 6'd04;
|
||||
40'b????????_????????_????????_????????_??1????? : accept_b_out_0 = 6'd05;
|
||||
40'b????????_????????_????????_????????_?1?????? : accept_b_out_0 = 6'd06;
|
||||
40'b????????_????????_????????_????????_1??????? : accept_b_out_0 = 6'd07;
|
||||
40'b????????_????????_????????_???????1_???????? : accept_b_out_0 = 6'd08;
|
||||
40'b????????_????????_????????_??????1?_???????? : accept_b_out_0 = 6'd09;
|
||||
40'b????????_????????_????????_?????1??_???????? : accept_b_out_0 = 6'd10;
|
||||
40'b????????_????????_????????_????1???_???????? : accept_b_out_0 = 6'd11;
|
||||
40'b????????_????????_????????_???1????_???????? : accept_b_out_0 = 6'd12;
|
||||
40'b????????_????????_????????_??1?????_???????? : accept_b_out_0 = 6'd13;
|
||||
40'b????????_????????_????????_?1??????_???????? : accept_b_out_0 = 6'd14;
|
||||
40'b????????_????????_????????_1???????_???????? : accept_b_out_0 = 6'd15;
|
||||
40'b????????_????????_???????1_????????_???????? : accept_b_out_0 = 6'd16;
|
||||
40'b????????_????????_??????1?_????????_???????? : accept_b_out_0 = 6'd17;
|
||||
40'b????????_????????_?????1??_????????_???????? : accept_b_out_0 = 6'd18;
|
||||
40'b????????_????????_????1???_????????_???????? : accept_b_out_0 = 6'd19;
|
||||
40'b????????_????????_???1????_????????_???????? : accept_b_out_0 = 6'd20;
|
||||
40'b????????_????????_??1?????_????????_???????? : accept_b_out_0 = 6'd21;
|
||||
40'b????????_????????_?1??????_????????_???????? : accept_b_out_0 = 6'd22;
|
||||
40'b????????_????????_1???????_????????_???????? : accept_b_out_0 = 6'd23;
|
||||
40'b????????_???????1_????????_????????_???????? : accept_b_out_0 = 6'd24;
|
||||
40'b????????_??????1?_????????_????????_???????? : accept_b_out_0 = 6'd25;
|
||||
40'b????????_?????1??_????????_????????_???????? : accept_b_out_0 = 6'd26;
|
||||
40'b????????_????1???_????????_????????_???????? : accept_b_out_0 = 6'd27;
|
||||
40'b????????_???1????_????????_????????_???????? : accept_b_out_0 = 6'd28;
|
||||
40'b????????_??1?????_????????_????????_???????? : accept_b_out_0 = 6'd29;
|
||||
40'b????????_?1??????_????????_????????_???????? : accept_b_out_0 = 6'd30;
|
||||
40'b????????_1???????_????????_????????_???????? : accept_b_out_0 = 6'd31;
|
||||
40'b???????1_????????_????????_????????_???????? : accept_b_out_0 = 6'd32;
|
||||
40'b??????1?_????????_????????_????????_???????? : accept_b_out_0 = 6'd33;
|
||||
40'b?????1??_????????_????????_????????_???????? : accept_b_out_0 = 6'd34;
|
||||
40'b????1???_????????_????????_????????_???????? : accept_b_out_0 = 6'd35;
|
||||
40'b???1????_????????_????????_????????_???????? : accept_b_out_0 = 6'd36;
|
||||
40'b??1?????_????????_????????_????????_???????? : accept_b_out_0 = 6'd37;
|
||||
40'b?1??????_????????_????????_????????_???????? : accept_b_out_0 = 6'd38;
|
||||
40'b1???????_????????_????????_????????_???????? : accept_b_out_0 = 6'd39;
|
||||
default: accept_b_out_0 = '1;
|
||||
endcase
|
||||
casez (accept_b_in)
|
||||
40'b????????_????????_????????_????????_???????1 : accept_b_out_1 = 56'd0000;
|
||||
40'b????????_????????_????????_????????_??????1? : accept_b_out_1 = 56'd0100;
|
||||
40'b????????_????????_????????_????????_?????1?? : accept_b_out_1 = 56'd0200;
|
||||
40'b????????_????????_????????_????????_????1??? : accept_b_out_1 = 56'd0300;
|
||||
40'b????????_????????_????????_????????_???1???? : accept_b_out_1 = 56'd0400;
|
||||
40'b????????_????????_????????_????????_??1????? : accept_b_out_1 = 56'd0500;
|
||||
40'b????????_????????_????????_????????_?1?????? : accept_b_out_1 = 56'd0600;
|
||||
40'b????????_????????_????????_????????_1??????? : accept_b_out_1 = 56'd0700;
|
||||
40'b????????_????????_????????_???????1_???????? : accept_b_out_1 = 56'd0800;
|
||||
40'b????????_????????_????????_??????1?_???????? : accept_b_out_1 = 56'd0900;
|
||||
40'b????????_????????_????????_?????1??_???????? : accept_b_out_1 = 56'd1000;
|
||||
40'b????????_????????_????????_????1???_???????? : accept_b_out_1 = 56'd1100;
|
||||
40'b????????_????????_????????_???1????_???????? : accept_b_out_1 = 56'd1200;
|
||||
40'b????????_????????_????????_??1?????_???????? : accept_b_out_1 = 56'd1300;
|
||||
40'b????????_????????_????????_?1??????_???????? : accept_b_out_1 = 56'd1400;
|
||||
40'b????????_????????_????????_1???????_???????? : accept_b_out_1 = 56'd1500;
|
||||
40'b????????_????????_???????1_????????_???????? : accept_b_out_1 = 56'd1600;
|
||||
40'b????????_????????_??????1?_????????_???????? : accept_b_out_1 = 56'd1700;
|
||||
40'b????????_????????_?????1??_????????_???????? : accept_b_out_1 = 56'd1800;
|
||||
40'b????????_????????_????1???_????????_???????? : accept_b_out_1 = 56'd1900;
|
||||
40'b????????_????????_???1????_????????_???????? : accept_b_out_1 = 56'd2000;
|
||||
40'b????????_????????_??1?????_????????_???????? : accept_b_out_1 = 56'd2100;
|
||||
40'b????????_????????_?1??????_????????_???????? : accept_b_out_1 = 56'd2200;
|
||||
40'b????????_????????_1???????_????????_???????? : accept_b_out_1 = 56'd2300;
|
||||
40'b????????_???????1_????????_????????_???????? : accept_b_out_1 = 56'd2400;
|
||||
40'b????????_??????1?_????????_????????_???????? : accept_b_out_1 = 56'd2500;
|
||||
40'b????????_?????1??_????????_????????_???????? : accept_b_out_1 = 56'd2600;
|
||||
40'b????????_????1???_????????_????????_???????? : accept_b_out_1 = 56'd2700;
|
||||
40'b????????_???1????_????????_????????_???????? : accept_b_out_1 = 56'd2800;
|
||||
40'b????????_??1?????_????????_????????_???????? : accept_b_out_1 = 56'd2900;
|
||||
40'b????????_?1??????_????????_????????_???????? : accept_b_out_1 = 56'd3000;
|
||||
40'b????????_1???????_????????_????????_???????? : accept_b_out_1 = 56'd3100;
|
||||
40'b???????1_????????_????????_????????_???????? : accept_b_out_1 = 56'd3200;
|
||||
40'b??????1?_????????_????????_????????_???????? : accept_b_out_1 = 56'd3300;
|
||||
40'b?????1??_????????_????????_????????_???????? : accept_b_out_1 = 56'd3400;
|
||||
40'b????1???_????????_????????_????????_???????? : accept_b_out_1 = 56'd3500;
|
||||
40'b???1????_????????_????????_????????_???????? : accept_b_out_1 = 56'd3600;
|
||||
40'b??1?????_????????_????????_????????_???????? : accept_b_out_1 = 56'd3700;
|
||||
40'b?1??????_????????_????????_????????_???????? : accept_b_out_1 = 56'd3800;
|
||||
40'b1???????_????????_????????_????????_???????? : accept_b_out_1 = 56'd3900;
|
||||
default: accept_b_out_1 = '1;
|
||||
endcase
|
||||
casez (accept_b_in)
|
||||
40'b????????_????????_????????_????????_???????1 : accept_b_out_2 = 143'd0000000000;
|
||||
40'b????????_????????_????????_????????_??????1? : accept_b_out_2 = 143'd0100000000;
|
||||
40'b????????_????????_????????_????????_?????1?? : accept_b_out_2 = 143'd0200000000;
|
||||
40'b????????_????????_????????_????????_????1??? : accept_b_out_2 = 143'd0300000000;
|
||||
40'b????????_????????_????????_????????_???1???? : accept_b_out_2 = 143'd0400000000;
|
||||
40'b????????_????????_????????_????????_??1????? : accept_b_out_2 = 143'd0500000000;
|
||||
40'b????????_????????_????????_????????_?1?????? : accept_b_out_2 = 143'd0600000000;
|
||||
40'b????????_????????_????????_????????_1??????? : accept_b_out_2 = 143'd0700000000;
|
||||
40'b????????_????????_????????_???????1_???????? : accept_b_out_2 = 143'd0800000000;
|
||||
40'b????????_????????_????????_??????1?_???????? : accept_b_out_2 = 143'd0900000000;
|
||||
40'b????????_????????_????????_?????1??_???????? : accept_b_out_2 = 143'd1000000000;
|
||||
40'b????????_????????_????????_????1???_???????? : accept_b_out_2 = 143'd1100000000;
|
||||
40'b????????_????????_????????_???1????_???????? : accept_b_out_2 = 143'd1200000000;
|
||||
40'b????????_????????_????????_??1?????_???????? : accept_b_out_2 = 143'd1300000000;
|
||||
40'b????????_????????_????????_?1??????_???????? : accept_b_out_2 = 143'd1400000000;
|
||||
40'b????????_????????_????????_1???????_???????? : accept_b_out_2 = 143'd1500000000;
|
||||
40'b????????_????????_???????1_????????_???????? : accept_b_out_2 = 143'd1600000000;
|
||||
40'b????????_????????_??????1?_????????_???????? : accept_b_out_2 = 143'd1700000000;
|
||||
40'b????????_????????_?????1??_????????_???????? : accept_b_out_2 = 143'd1800000000;
|
||||
40'b????????_????????_????1???_????????_???????? : accept_b_out_2 = 143'd1900000000;
|
||||
40'b????????_????????_???1????_????????_???????? : accept_b_out_2 = 143'd2000000000;
|
||||
40'b????????_????????_??1?????_????????_???????? : accept_b_out_2 = 143'd2100000000;
|
||||
40'b????????_????????_?1??????_????????_???????? : accept_b_out_2 = 143'd2200000000;
|
||||
40'b????????_????????_1???????_????????_???????? : accept_b_out_2 = 143'd2300000000;
|
||||
40'b????????_???????1_????????_????????_???????? : accept_b_out_2 = 143'd2400000000;
|
||||
40'b????????_??????1?_????????_????????_???????? : accept_b_out_2 = 143'd2500000000;
|
||||
40'b????????_?????1??_????????_????????_???????? : accept_b_out_2 = 143'd2600000000;
|
||||
40'b????????_????1???_????????_????????_???????? : accept_b_out_2 = 143'd2700000000;
|
||||
40'b????????_???1????_????????_????????_???????? : accept_b_out_2 = 143'd2800000000;
|
||||
40'b????????_??1?????_????????_????????_???????? : accept_b_out_2 = 143'd2900000000;
|
||||
40'b????????_?1??????_????????_????????_???????? : accept_b_out_2 = 143'd3000000000;
|
||||
40'b????????_1???????_????????_????????_???????? : accept_b_out_2 = 143'd3100000000;
|
||||
40'b???????1_????????_????????_????????_???????? : accept_b_out_2 = 143'd3200000000;
|
||||
40'b??????1?_????????_????????_????????_???????? : accept_b_out_2 = 143'd3300000000;
|
||||
40'b?????1??_????????_????????_????????_???????? : accept_b_out_2 = 143'd3400000000;
|
||||
40'b????1???_????????_????????_????????_???????? : accept_b_out_2 = 143'd3500000000;
|
||||
40'b???1????_????????_????????_????????_???????? : accept_b_out_2 = 143'd3600000000;
|
||||
40'b??1?????_????????_????????_????????_???????? : accept_b_out_2 = 143'd3700000000;
|
||||
40'b?1??????_????????_????????_????????_???????? : accept_b_out_2 = 143'd3800000000;
|
||||
40'b1???????_????????_????????_????????_???????? : accept_b_out_2 = 143'd3900000000;
|
||||
default: accept_b_out_2 = '1;
|
||||
endcase
|
||||
end
|
||||
assign accept_b_ref_0 = 6'(cyc[5:1]);
|
||||
assign accept_b_ref_1 = 56'(cyc[5:1]) * 56'd100;
|
||||
assign accept_b_ref_2 = 143'(cyc[5:1]) * 143'd100000000;
|
||||
|
||||
// Accept C: a 155-bit (W) selector decoded into outputs of three widths (I/Q/W result).
|
||||
wire [154:0] accept_c_in = 155'b1 << cyc[5:0];
|
||||
logic [ 5:0] accept_c_out_0, accept_c_ref_0;
|
||||
logic [ 55:0] accept_c_out_1, accept_c_ref_1;
|
||||
logic [142:0] accept_c_out_2, accept_c_ref_2;
|
||||
always_comb begin
|
||||
casez (accept_c_in)
|
||||
155'b????????_????????_????????_????????_???????1 : accept_c_out_0 = 6'd00;
|
||||
155'b????????_????????_????????_????????_??????1? : accept_c_out_0 = 6'd01;
|
||||
155'b????????_????????_????????_????????_?????1?? : accept_c_out_0 = 6'd02;
|
||||
155'b????????_????????_????????_????????_????1??? : accept_c_out_0 = 6'd03;
|
||||
155'b????????_????????_????????_????????_???1???? : accept_c_out_0 = 6'd04;
|
||||
155'b????????_????????_????????_????????_??1????? : accept_c_out_0 = 6'd05;
|
||||
155'b????????_????????_????????_????????_?1?????? : accept_c_out_0 = 6'd06;
|
||||
155'b????????_????????_????????_????????_1??????? : accept_c_out_0 = 6'd07;
|
||||
155'b????????_????????_????????_???????1_???????? : accept_c_out_0 = 6'd08;
|
||||
155'b????????_????????_????????_??????1?_???????? : accept_c_out_0 = 6'd09;
|
||||
155'b????????_????????_????????_?????1??_???????? : accept_c_out_0 = 6'd10;
|
||||
default: accept_c_out_0 = '1;
|
||||
endcase
|
||||
casez (accept_c_in)
|
||||
155'b????????_????????_????????_????????_???????1 : accept_c_out_1 = 56'd0000;
|
||||
155'b????????_????????_????????_????????_??????1? : accept_c_out_1 = 56'd0100;
|
||||
155'b????????_????????_????????_????????_?????1?? : accept_c_out_1 = 56'd0200;
|
||||
155'b????????_????????_????????_????????_????1??? : accept_c_out_1 = 56'd0300;
|
||||
155'b????????_????????_????????_????????_???1???? : accept_c_out_1 = 56'd0400;
|
||||
155'b????????_????????_????????_????????_??1????? : accept_c_out_1 = 56'd0500;
|
||||
155'b????????_????????_????????_????????_?1?????? : accept_c_out_1 = 56'd0600;
|
||||
155'b????????_????????_????????_????????_1??????? : accept_c_out_1 = 56'd0700;
|
||||
155'b????????_????????_????????_???????1_???????? : accept_c_out_1 = 56'd0800;
|
||||
155'b????????_????????_????????_??????1?_???????? : accept_c_out_1 = 56'd0900;
|
||||
155'b????????_????????_????????_?????1??_???????? : accept_c_out_1 = 56'd1000;
|
||||
default: accept_c_out_1 = '1;
|
||||
endcase
|
||||
casez (accept_c_in)
|
||||
155'b????????_????????_????????_????????_???????1 : accept_c_out_2 = 143'd0000000000;
|
||||
155'b????????_????????_????????_????????_??????1? : accept_c_out_2 = 143'd0100000000;
|
||||
155'b????????_????????_????????_????????_?????1?? : accept_c_out_2 = 143'd0200000000;
|
||||
155'b????????_????????_????????_????????_????1??? : accept_c_out_2 = 143'd0300000000;
|
||||
155'b????????_????????_????????_????????_???1???? : accept_c_out_2 = 143'd0400000000;
|
||||
155'b????????_????????_????????_????????_??1????? : accept_c_out_2 = 143'd0500000000;
|
||||
155'b????????_????????_????????_????????_?1?????? : accept_c_out_2 = 143'd0600000000;
|
||||
155'b????????_????????_????????_????????_1??????? : accept_c_out_2 = 143'd0700000000;
|
||||
155'b????????_????????_????????_???????1_???????? : accept_c_out_2 = 143'd0800000000;
|
||||
155'b????????_????????_????????_??????1?_???????? : accept_c_out_2 = 143'd0900000000;
|
||||
155'b????????_????????_????????_?????1??_???????? : accept_c_out_2 = 143'd1000000000;
|
||||
default: accept_c_out_2 = '1;
|
||||
endcase
|
||||
end
|
||||
assign accept_c_ref_0 = cyc[5:0] <= 6'd10 ? 6'(cyc[5:0]) : ~6'd0;
|
||||
assign accept_c_ref_1 = cyc[5:0] <= 6'd10 ? 56'(cyc[5:0]) * 56'd100 : ~56'd0;
|
||||
assign accept_c_ref_2 = cyc[5:0] <= 6'd10 ? 143'(cyc[5:0]) * 143'd100000000 : ~143'd0;
|
||||
|
||||
// Accept D: the default value is set before the case, with no default item.
|
||||
wire [39:0] accept_d_in = 40'b1 << cyc[4:0];
|
||||
logic [4:0] accept_d_out, accept_d_ref;
|
||||
always_comb begin
|
||||
accept_d_out = '1;
|
||||
casez (accept_d_in)
|
||||
40'b????????_????????_????????_????????_???????1 : accept_d_out = 5'd0;
|
||||
40'b????????_????????_????????_????????_??????1? : accept_d_out = 5'd1;
|
||||
40'b????????_????????_????????_????????_?????1?? : accept_d_out = 5'd2;
|
||||
40'b????????_????????_????????_????????_????1??? : accept_d_out = 5'd3;
|
||||
endcase
|
||||
end
|
||||
assign accept_d_ref = cyc[4:0] <= 5'd3 ? 5'(cyc[4:0]) : 5'h1f;
|
||||
|
||||
// Accept E: an empty default item.
|
||||
wire [39:0] accept_e_in = 40'b1 << cyc[4:0];
|
||||
logic [4:0] accept_e_out, accept_e_ref;
|
||||
always_comb begin
|
||||
accept_e_out = '1;
|
||||
casez (accept_e_in)
|
||||
40'b????????_????????_????????_????????_???????1 : accept_e_out = 5'd0;
|
||||
40'b????????_????????_????????_????????_??????1? : accept_e_out = 5'd1;
|
||||
40'b????????_????????_????????_????????_?????1?? : accept_e_out = 5'd2;
|
||||
40'b????????_????????_????????_????????_????1??? : accept_e_out = 5'd3;
|
||||
default: begin end
|
||||
endcase
|
||||
end
|
||||
assign accept_e_ref = cyc[4:0] <= 5'd3 ? 5'(cyc[4:0]) : 5'h1f;
|
||||
|
||||
// Accept F: an item with an empty body (output keeps its pre-case default).
|
||||
wire [39:0] accept_f_in = 40'b1 << cyc[4:0];
|
||||
logic [4:0] accept_f_out, accept_f_ref;
|
||||
always_comb begin
|
||||
accept_f_out = '1;
|
||||
casez (accept_f_in)
|
||||
40'b????????_????????_????????_????????_???1???? : begin end
|
||||
40'b????????_????????_????????_????????_???????1 : accept_f_out = 5'd0;
|
||||
40'b????????_????????_????????_????????_??????1? : accept_f_out = 5'd1;
|
||||
40'b????????_????????_????????_????????_?????1?? : accept_f_out = 5'd2;
|
||||
40'b????????_????????_????????_????????_????1??? : accept_f_out = 5'd3;
|
||||
default: begin end
|
||||
endcase
|
||||
end
|
||||
assign accept_f_ref = cyc[4:0] <= 5'd3 ? 5'(cyc[4:0]) : 5'h1f;
|
||||
|
||||
// Accept G: non-blocking assignments.
|
||||
wire [23:0] accept_g_in = 24'b1 << cyc[4:0];
|
||||
logic [5:0] accept_g_out, accept_g_ref;
|
||||
always_ff @(posedge clk) begin
|
||||
accept_g_out <= '1;
|
||||
casez (accept_g_in)
|
||||
24'b????????_????????_???????1 : accept_g_out <= 6'd0;
|
||||
24'b????????_????????_??????1? : accept_g_out <= 6'd1;
|
||||
24'b????????_????????_?????1?? : accept_g_out <= 6'd2;
|
||||
24'b????????_????????_????1??? : accept_g_out <= 6'd3;
|
||||
endcase
|
||||
end
|
||||
always_ff @(posedge clk)
|
||||
accept_g_ref <= cyc[4:0] == 5'd0 ? 6'd0 : cyc[4:0] == 5'd1 ? 6'd1
|
||||
: cyc[4:0] == 5'd2 ? 6'd2 : cyc[4:0] == 5'd3 ? 6'd3 : ~6'd0;
|
||||
|
||||
// Accept H: multiple outputs from one decoder, some items assigning only a subset.
|
||||
wire [23:0] accept_h_in = 24'b1 << cyc[4:0];
|
||||
logic [ 5:0] accept_h_out_0, accept_h_ref_0;
|
||||
logic [11:0] accept_h_out_1, accept_h_ref_1;
|
||||
always_comb begin
|
||||
accept_h_out_0 = '1;
|
||||
accept_h_out_1 = '1;
|
||||
casez (accept_h_in)
|
||||
24'b????????_????????_???????1 : begin accept_h_out_0 = 6'd0; accept_h_out_1 = 12'h001; end
|
||||
24'b????????_????????_??????1? : accept_h_out_0 = 6'd1;
|
||||
24'b????????_????????_?????1?? : accept_h_out_1 = 12'h004;
|
||||
24'b????????_????????_????1??? : begin accept_h_out_0 = 6'd3; accept_h_out_1 = 12'h008; end
|
||||
endcase
|
||||
end
|
||||
assign accept_h_ref_0 = cyc[4:0] == 5'd0 ? 6'd0 : cyc[4:0] == 5'd1 ? 6'd1
|
||||
: cyc[4:0] == 5'd3 ? 6'd3 : ~6'd0;
|
||||
assign accept_h_ref_1 = cyc[4:0] == 5'd0 ? 12'h001 : cyc[4:0] == 5'd2 ? 12'h004
|
||||
: cyc[4:0] == 5'd3 ? 12'h008 : ~12'd0;
|
||||
|
||||
// Accept I: multiple outputs from one decoder, non-blocking assignments.
|
||||
wire [23:0] accept_i_in = 24'b1 << cyc[4:0];
|
||||
logic [ 5:0] accept_i_out_0, accept_i_ref_0;
|
||||
logic [11:0] accept_i_out_1, accept_i_ref_1;
|
||||
always_ff @(posedge clk) begin
|
||||
accept_i_out_0 <= '1;
|
||||
accept_i_out_1 <= '1;
|
||||
casez (accept_i_in)
|
||||
24'b????????_????????_???????1 : begin accept_i_out_0 <= 6'd0; accept_i_out_1 <= 12'h001; end
|
||||
24'b????????_????????_??????1? : begin accept_i_out_0 <= 6'd1; accept_i_out_1 <= 12'h002; end
|
||||
24'b????????_????????_?????1?? : begin accept_i_out_0 <= 6'd2; accept_i_out_1 <= 12'h004; end
|
||||
24'b????????_????????_????1??? : begin accept_i_out_0 <= 6'd3; accept_i_out_1 <= 12'h008; end
|
||||
endcase
|
||||
end
|
||||
always_ff @(posedge clk) begin
|
||||
accept_i_ref_0 <= cyc[4:0] == 5'd0 ? 6'd0 : cyc[4:0] == 5'd1 ? 6'd1
|
||||
: cyc[4:0] == 5'd2 ? 6'd2 : cyc[4:0] == 5'd3 ? 6'd3 : ~6'd0;
|
||||
accept_i_ref_1 <= cyc[4:0] == 5'd0 ? 12'h001 : cyc[4:0] == 5'd1 ? 12'h002
|
||||
: cyc[4:0] == 5'd2 ? 12'h004 : cyc[4:0] == 5'd3 ? 12'h008 : ~12'd0;
|
||||
end
|
||||
|
||||
// Accept J: an item that can never match in 2-state (an X in casez) is skipped.
|
||||
// verilator lint_off CASEWITHX
|
||||
wire [23:0] accept_j_in = 24'b1 << cyc[4:0];
|
||||
logic [5:0] accept_j_out, accept_j_ref;
|
||||
always_comb begin
|
||||
accept_j_out = '1;
|
||||
casez (accept_j_in)
|
||||
24'b????????_????????_???????x : accept_j_out = 6'd9; // X never matches in 2-state, skipped
|
||||
24'b????????_????????_??????1? : accept_j_out = 6'd1;
|
||||
24'b????????_????????_?????1?? : accept_j_out = 6'd2;
|
||||
24'b????????_????????_????1??? : accept_j_out = 6'd3;
|
||||
endcase
|
||||
end
|
||||
// verilator lint_on CASEWITHX
|
||||
assign accept_j_ref = cyc[4:0] == 5'd1 ? 6'd1 : cyc[4:0] == 5'd2 ? 6'd2
|
||||
: cyc[4:0] == 5'd3 ? 6'd3 : ~6'd0;
|
||||
|
||||
// The cases below are intentionally NOT converted to a decoder.
|
||||
|
||||
// Reject A: too few conditions to be worth the indexed load.
|
||||
wire [23:0] reject_a_in = 24'b1 << cyc[4:0];
|
||||
logic [5:0] reject_a_out, reject_a_ref;
|
||||
always_comb begin
|
||||
reject_a_out = '1;
|
||||
casez (reject_a_in)
|
||||
24'b????????_????????_???????1 : reject_a_out = 6'd0;
|
||||
24'b????????_????????_??????1? : reject_a_out = 6'd1;
|
||||
endcase
|
||||
end
|
||||
assign reject_a_ref = cyc[4:0] == 5'd0 ? 6'd0 : cyc[4:0] == 5'd1 ? 6'd1 : ~6'd0;
|
||||
|
||||
// Reject B: every condition contains an X, so none can ever match in 2-state. The case has no
|
||||
// matchable branches, so it must not be treated as a decoder - it just keeps the
|
||||
// pre-case default.
|
||||
// verilator lint_off CASEWITHX
|
||||
wire [23:0] reject_b_in = 24'b1 << cyc[4:0];
|
||||
logic [5:0] reject_b_out, reject_b_ref;
|
||||
always_comb begin
|
||||
reject_b_out = 6'h2a;
|
||||
casez (reject_b_in)
|
||||
24'b????????_????????_???????x : reject_b_out = 6'd0;
|
||||
24'b????????_????????_??????x? : reject_b_out = 6'd1;
|
||||
24'b????????_????????_?????x?? : reject_b_out = 6'd2;
|
||||
endcase
|
||||
end
|
||||
// verilator lint_on CASEWITHX
|
||||
assign reject_b_ref = 6'h2a; // No condition can ever match, so always the pre-default
|
||||
|
||||
// Test driver/checker
|
||||
always @(posedge clk) begin
|
||||
`checkh(accept_a_out_0, accept_a_ref_0);
|
||||
`checkh(accept_a_out_1, accept_a_ref_1);
|
||||
`checkh(accept_a_out_2, accept_a_ref_2);
|
||||
`checkh(accept_b_out_0, accept_b_ref_0);
|
||||
`checkh(accept_b_out_1, accept_b_ref_1);
|
||||
`checkh(accept_b_out_2, accept_b_ref_2);
|
||||
`checkh(accept_c_out_0, accept_c_ref_0);
|
||||
`checkh(accept_c_out_1, accept_c_ref_1);
|
||||
`checkh(accept_c_out_2, accept_c_ref_2);
|
||||
`checkh(accept_d_out, accept_d_ref);
|
||||
`checkh(accept_e_out, accept_e_ref);
|
||||
`checkh(accept_f_out, accept_f_ref);
|
||||
`checkh(accept_g_out, accept_g_ref);
|
||||
`checkh(accept_h_out_0, accept_h_ref_0);
|
||||
`checkh(accept_h_out_1, accept_h_ref_1);
|
||||
`checkh(accept_i_out_0, accept_i_ref_0);
|
||||
`checkh(accept_i_out_1, accept_i_ref_1);
|
||||
`checkh(accept_j_out, accept_j_ref);
|
||||
`checkh(reject_a_out, reject_a_ref);
|
||||
`checkh(reject_b_out, reject_b_ref);
|
||||
|
||||
cyc <= cyc + 32'd1;
|
||||
if (cyc[16]) begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
#!/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('vlt')
|
||||
|
||||
test.top_filename = "t/t_case_decoder.v"
|
||||
|
||||
test.compile(verilator_flags2=['--binary', '--stats', '-fno-case-decoder'])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.file_grep(test.stats, r'Optimizations, Cases decoder\s+(\d+)', 0)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -11,7 +11,7 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--stats", "-fno-case-table"])
|
||||
test.compile(verilator_flags2=["--stats", "-fno-case-table", "-fno-case-decoder"])
|
||||
|
||||
if test.vlt_all:
|
||||
test.file_grep(test.stats, r'Optimizations, Tables created\s+(\d+)', 1)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--stats", "-fno-case-table"])
|
||||
test.compile(verilator_flags2=["--stats", "-fno-case-table", "-fno-case-decoder"])
|
||||
|
||||
if test.vlt_all:
|
||||
test.file_grep(test.stats, r'Optimizations, Tables created\s+(\d+)', 1)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--stats", "-fno-case-table"])
|
||||
test.compile(verilator_flags2=["--stats", "-fno-case-table", "-fno-case-decoder"])
|
||||
|
||||
if test.vlt_all:
|
||||
test.file_grep(test.stats, r'Optimizations, Tables created\s+(\d+)', 1)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--stats", "-fno-case-table"])
|
||||
test.compile(verilator_flags2=["--stats", "-fno-case-table", "-fno-case-decoder"])
|
||||
|
||||
if test.vlt_all:
|
||||
test.file_grep(test.stats, r'Optimizations, Tables created\s+(\d+)', 2)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--stats", "-fno-case-table"])
|
||||
test.compile(verilator_flags2=["--stats", "-fno-case-table", "-fno-case-decoder"])
|
||||
|
||||
if test.vlt_all:
|
||||
test.file_grep(test.stats, r'Optimizations, Tables created\s+(\d+)', 1)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--stats", "-fno-case-table"])
|
||||
test.compile(verilator_flags2=["--stats", "-fno-case-table", "-fno-case-decoder"])
|
||||
|
||||
if test.vlt_all:
|
||||
test.file_grep(test.stats, r'Optimizations, Tables created\s+(\d+)', 1)
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import vltest_bootstrap
|
|||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--stats", "-fno-case-table"])
|
||||
test.compile(verilator_flags2=["--stats", "-fno-case-table", "-fno-case-decoder"])
|
||||
|
||||
if test.vlt_all:
|
||||
test.file_grep(test.stats, r'Optimizations, Tables created\s+(\d+)', 1)
|
||||
|
|
|
|||
Loading…
Reference in New Issue