Support delayed tristate gates (#7960) (#7961)

Fixes #7960.
This commit is contained in:
Patrick Creighton 2026-07-20 06:21:23 -07:00 committed by GitHub
parent 176e333774
commit cdfff86c2d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 67 additions and 9 deletions

View File

@ -422,9 +422,11 @@ class TristateVisitor final : public TristateBaseVisitor {
struct RefStrength final {
AstNodeVarRef* m_varrefp;
VStrength m_strength;
RefStrength(AstNodeVarRef* varrefp, VStrength strength)
AstDelay* m_delayp; // Explicit delay on the continuous assignment
RefStrength(AstNodeVarRef* varrefp, VStrength strength, AstDelay* delayp)
: m_varrefp{varrefp}
, m_strength{strength} {}
, m_strength{strength}
, m_delayp{delayp} {}
};
using RefStrengthVec = std::vector<RefStrength>;
using VarMap = std::map<AstVar*, RefStrengthVec*>;
@ -449,6 +451,7 @@ class TristateVisitor final : public TristateBaseVisitor {
bool m_processedIfaces = false; // Interface modules already processed (interfaces-first pass)
VStrength m_currentStrength = VStrength::STRONG; // Current strength of assignment,
// Used only on LHS of assignment
AstDelay* m_currentDelayp = nullptr; // Delay of assignment currently collecting LHS drivers
const AstNode* m_logicp = nullptr; // Current logic being built
TristateGraph m_tgraph; // Logic graph
// Map: interface AstVar* -> list of per-module (enVarp, outVarp) contribution pairs
@ -635,7 +638,7 @@ class TristateVisitor final : public TristateBaseVisitor {
AstVar* const key = nodep->varp();
const auto pair = m_lhsmap.emplace(key, nullptr);
if (pair.second) pair.first->second = new RefStrengthVec;
pair.first->second->push_back(RefStrength{nodep, m_currentStrength});
pair.first->second->push_back(RefStrength{nodep, m_currentStrength, m_currentDelayp});
}
AstNodeExpr* newEnableDeposit(AstSel* selp, AstNodeExpr* enp) {
@ -854,6 +857,7 @@ class TristateVisitor final : public TristateBaseVisitor {
AstAssignW* const enLhspAssignp = new AstAssignW{
refp->fileline(), new AstVarRef{refp->fileline(), newEnLhsp, VAccess::WRITE},
getEnp(refp)};
if (it->m_delayp) { enLhspAssignp->timingControlp(it->m_delayp->cloneTree(false)); }
UINFO(9, " newenlhspAssignp " << enLhspAssignp);
nodep->addStmtsp(new AstAlways{enLhspAssignp});
@ -1567,11 +1571,18 @@ class TristateVisitor final : public TristateBaseVisitor {
void visitAssign(AstNodeAssign* nodep) {
VL_RESTORER(m_alhs);
VL_RESTORER(m_currentStrength);
VL_RESTORER(m_currentDelayp);
if (VN_IS(nodep->rhsp(), CReset)) return;
if (AstAssignW* const assignWp = VN_CAST(nodep, AssignW)) {
m_currentDelayp = VN_CAST(assignWp->timingControlp(), Delay);
}
if (m_graphing) {
if (AstAssignW* assignWp = VN_CAST(nodep, AssignW)) {
if (assignWp->timingControlp() || assignWp->getLhsNetDelay()) return;
addToAssignmentList(assignWp);
if (assignWp->getLhsNetDelay()) return;
if (assignWp->timingControlp() && !m_currentDelayp) return;
// Separate data and enable assignments cannot preserve distinct rise/fall delays.
if (m_currentDelayp && m_currentDelayp->fallDelay()) return;
if (!assignWp->timingControlp()) addToAssignmentList(assignWp);
}
if (nodep->user2() & U2_GRAPHING) return;

View File

@ -4,26 +4,73 @@
// SPDX-FileCopyrightText: 2026 Antmicro
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__, `__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0)
`define check_scalar(exp) do begin `checkh(out_assign, exp); `checkh(out_buf, exp); `checkh(out_net, exp); end while(0)
// verilog_format: on
module t;
parameter delay = 0;
localparam int TRI_DELAY = 3;
logic in = 0;
logic [3:0] in_vec = 4'h0;
logic tri_data = 0;
logic tri_enable = 0;
wire out_assign;
wire out_buf;
wire #(5,3.3) out_net;
wire out_bufif0;
wire out_bufif1;
wire out_issue;
wire out_nmos;
wire out_notif0;
wire out_notif1;
wire out_pmos;
wire #(5, 3.3) out_net;
wire [3:0] out_vec_assign;
assign #(5,3) out_assign = in;
buf #(5,3) u_buf (out_buf, in);
assign #(5, 3) out_assign = in;
buf #(5, 3) u_buf (out_buf, in);
bufif0 #(delay) u_bufif0 (out_bufif0, in, 1'b0);
bufif1 #(TRI_DELAY) u_bufif1 (out_bufif1, tri_data, tri_enable);
bufif1 #(delay) u_issue (out_issue, in, 1'b0);
nmos #(delay) u_nmos (out_nmos, in, 1'b1);
notif0 #(delay) u_notif0 (out_notif0, in, 1'b0);
notif1 #(delay) u_notif1 (out_notif1, in, 1'b1);
pmos #(delay) u_pmos (out_pmos, in, 1'b0);
assign out_net = in;
assign #(5,3) out_vec_assign = in_vec;
assign #(5, 3) out_vec_assign = in_vec;
initial begin
#4;
`checkh(out_bufif1, 1'bz);
tri_enable = 1'b1;
#2;
`checkh(out_bufif1, 1'bz);
#1;
`checkh(out_bufif1, 1'b0);
tri_data = 1'b1;
#2;
`checkh(out_bufif1, 1'b0);
#1;
`checkh(out_bufif1, 1'b1);
tri_enable = 1'b0;
#2;
`checkh(out_bufif1, 1'b1);
#1;
`checkh(out_bufif1, 1'bz);
end
initial begin
#4;
`check_scalar(1'b0);
`checkh(out_bufif0, 1'b0);
`checkh(out_issue, 1'bz);
`checkh(out_nmos, 1'b0);
`checkh(out_notif0, 1'b1);
`checkh(out_notif1, 1'b1);
`checkh(out_pmos, 1'b0);
`checkh(out_vec_assign, 4'h0);
// Rise canceled by a fall before the rise delay expires.