diff --git a/Changes b/Changes index d7532cb9f..a3bf6a698 100644 --- a/Changes +++ b/Changes @@ -21,6 +21,7 @@ Verilator 5.037 devel * Support `$timeformat` with missing arguments (#6113). [Alex Solomatnikov] * Support `specparam` (#5767). * Support parameter forward types. +* Support constant functions with left-hand-side concatenates. * Add PROCINITASSIGN on initial assignments to process variables (#2481). [Niraj Menon] * Add BADVLTPRAGMA on unknown Verilator pragmas (#5945). [Shou-Li Hsu] * Add ternary operator into branch coverage (#5880). [Ryszard Rozak, Antmicro Ltd.] diff --git a/src/V3Simulate.h b/src/V3Simulate.h index f92183ea8..106a83ca9 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -696,15 +696,14 @@ private: } } - void handleAssignArray(AstNodeAssign* nodep, AstArraySel* selp) { - iterateAndNextConstNull(nodep->rhsp()); // Value to assign + void handleAssignArray(AstNodeAssign* nodep, AstArraySel* selp, AstNodeExpr* valueFromp) { // At present we only handle single dimensional assignments // To do better, we need the concept of lvalues, or similar, to know where/how to insert checkNodeInfo(selp); iterateAndNextConstNull(selp->bitp()); // Bit index AstVarRef* const varrefp = VN_CAST(selp->fromp(), VarRef); if (!varrefp) { - clearOptimizable(nodep, "Array select LHS isn't simple variable"); + clearOptimizable(selp->fromp(), "Array select LHS isn't simple variable"); return; } AstUnpackArrayDType* const arrayp @@ -736,7 +735,7 @@ private: m_reclaimValuesp.push_back(initp); } const uint32_t index = fetchConst(selp->bitp())->toUInt(); - AstNodeExpr* const valuep = newTrackedClone(fetchValue(nodep->rhsp())); + AstNodeExpr* const valuep = newTrackedClone(fetchValue(valueFromp)); UINFO(9, " set val[" << index << "] = " << valuep); // Values are in the "real" tree under the InitArray so can eventually extract it, // Not in the usual setValue (via m_varAux) @@ -745,10 +744,9 @@ private: assignOutValue(nodep, vscp, initp); } } - void handleAssignSel(AstNodeAssign* nodep, AstSel* selp) { + void handleAssignSel(AstNodeAssign* nodep, AstSel* selp, AstNodeExpr* valueFromp) { AstVarRef* varrefp = nullptr; V3Number lsb{nodep}; - iterateAndNextConstNull(nodep->rhsp()); // Value to assign handleAssignSelRecurse(nodep, selp, varrefp /*ref*/, lsb /*ref*/, 0); if (!m_checkOnly && optimizable()) { UASSERT_OBJ(varrefp, nodep, @@ -768,7 +766,7 @@ private: outconstp->num().setAllBitsX(); } } - outconstp->num().opSelInto(fetchConst(nodep->rhsp())->num(), lsb, selp->widthConst()); + outconstp->num().opSelInto(fetchConst(valueFromp)->num(), lsb, selp->widthConst()); assignOutValue(nodep, vscp, outconstp); } } @@ -790,10 +788,73 @@ private: lsbRef.opAdd(sublsb, fetchConst(selp->lsbp())->num()); } } else { - clearOptimizable(nodep, "Select LHS isn't simple variable"); + clearOptimizable(selp->fromp(), "Select LHS isn't simple variable"); + } + } + void handleAssignRecurse(AstNodeAssign* nodep, AstNodeExpr* lhsp, AstNodeExpr* valueFromp) { + if (!optimizable()) return; + if (AstArraySel* const selp = VN_CAST(lhsp, ArraySel)) { + if (!m_params) { + clearOptimizable(lhsp, "Assign LHS has select"); + return; + } + handleAssignArray(nodep, selp, valueFromp); + } else if (AstConcat* const selp = VN_CAST(lhsp, Concat)) { + checkNodeInfo(selp); + AstBasicDType* const rhsBasicp + = VN_CAST(selp->rhsp()->dtypep()->skipRefp(), BasicDType); + if (!rhsBasicp) { + clearOptimizable(lhsp, "Assign LHS concat of non-basic type"); + return; + } + // Split value into left and right concat values + if (!m_checkOnly) { + { + AstConst* const outconstp + = new AstConst{selp->lhsp()->fileline(), AstConst::WidthedValue{}, + selp->lhsp()->width(), 0}; + outconstp->num().opSel(fetchConst(valueFromp)->num(), + selp->lhsp()->width() + selp->rhsp()->width() + 1, + selp->rhsp()->width()); + newValue(selp->lhsp(), outconstp); + } + { + AstConst* const outconstp + = new AstConst{selp->rhsp()->fileline(), AstConst::WidthedValue{}, + selp->rhsp()->widthMin(), 0}; + outconstp->num().opSel(fetchConst(valueFromp)->num(), + selp->rhsp()->width() - 1, 0); + newValue(selp->rhsp(), outconstp); + } + } + handleAssignRecurse(nodep, selp->lhsp(), selp->lhsp()); + handleAssignRecurse(nodep, selp->rhsp(), selp->rhsp()); + } else if (AstReplicate* const selp = VN_CAST(lhsp, Replicate)) { + checkNodeInfo(selp); + iterateAndNextConstNull(selp->countp()); + AstConst* const countp = VN_CAST(selp->countp(), Const); + if (!countp || !countp->num().isEqOne()) { + clearOptimizable(selp, "Replicate LHS count isn't one"); + return; + } + handleAssignRecurse(nodep, selp->srcp(), valueFromp); + } else if (AstSel* const selp = VN_CAST(lhsp, Sel)) { + if (!m_params) { + clearOptimizable(lhsp, "Assign LHS has select"); + return; + } + handleAssignSel(nodep, selp, valueFromp); + } else if (VN_IS(lhsp, VarRef)) { + if (m_checkOnly) { + iterateAndNextConstNull(lhsp); + } else { + AstNode* const vscp = varOrScope(VN_CAST(lhsp, VarRef)); + assignOutValue(nodep, vscp, fetchValue(valueFromp)); + } + } else { + clearOptimizable(lhsp, "Assign LHS isn't simple variable"); } } - void visit(AstNodeAssign* nodep) override { if (jumpingOver(nodep)) return; if (!optimizable()) return; // Accelerate @@ -812,28 +873,8 @@ private: m_anyAssignComb = true; } - if (AstSel* const selp = VN_CAST(nodep->lhsp(), Sel)) { - if (!m_params) { - clearOptimizable(nodep, "LHS has select"); - return; - } - handleAssignSel(nodep, selp); - } else if (AstArraySel* const selp = VN_CAST(nodep->lhsp(), ArraySel)) { - if (!m_params) { - clearOptimizable(nodep, "LHS has select"); - return; - } - handleAssignArray(nodep, selp); - } else if (!VN_IS(nodep->lhsp(), VarRef)) { - clearOptimizable(nodep, "LHS isn't simple variable"); - } else if (m_checkOnly) { - iterateChildrenConst(nodep); - } else if (optimizable()) { - iterateAndNextConstNull(nodep->rhsp()); - if (!optimizable()) return; - AstNode* const vscp = varOrScope(VN_CAST(nodep->lhsp(), VarRef)); - assignOutValue(nodep, vscp, fetchValue(nodep->rhsp())); - } + iterateAndNextConstNull(nodep->rhsp()); // Value to assign + handleAssignRecurse(nodep, nodep->lhsp(), nodep->rhsp()); } void visit(AstArraySel* nodep) override { checkNodeInfo(nodep); diff --git a/test_regress/t/t_func_const.v b/test_regress/t/t_func_const.v index 2d8487195..d1abd415e 100644 --- a/test_regress/t/t_func_const.v +++ b/test_regress/t/t_func_const.v @@ -19,6 +19,7 @@ module t; localparam P6 = f_return(P4); localparam P3 = 3; localparam P128 = f_package(); + localparam [15:0] PSEL = f_concat_sel(16'h4321); typedef struct packed { logic [7:0] data; @@ -50,6 +51,7 @@ module t; if (bigparam.second != 1'b0) $stop; if (bigparam.data != 32'hfff12fff) $stop; if (P128 != 128) $stop; + if (PSEL != 16'h1234) $stop; $write("*-* All Finished *-*\n"); $finish; end @@ -127,4 +129,11 @@ module t; result.second = second; return result; endfunction + + function [15:0] f_concat_sel(input [15:0] in); + reg [3:0] tmp1, tmp2, tmp3, tmp4; + {tmp4, tmp3, tmp2, tmp1} = in; + f_concat_sel = {tmp1, tmp2, tmp3, tmp4}; + endfunction + endmodule