Process only interfaces in the first tristate pass; no_optimize test drivers
This commit is contained in:
parent
f097d50059
commit
f6a63db884
|
|
@ -446,6 +446,7 @@ class TristateVisitor final : public TristateBaseVisitor {
|
||||||
int m_unique = 0;
|
int m_unique = 0;
|
||||||
bool m_alhs = false; // On LHS of assignment
|
bool m_alhs = false; // On LHS of assignment
|
||||||
bool m_inAlias = false; // Inside alias statement
|
bool m_inAlias = false; // Inside alias statement
|
||||||
|
bool m_processedIfaces = false; // Interface modules already processed (interfaces-first pass)
|
||||||
VStrength m_currentStrength = VStrength::STRONG; // Current strength of assignment,
|
VStrength m_currentStrength = VStrength::STRONG; // Current strength of assignment,
|
||||||
// Used only on LHS of assignment
|
// Used only on LHS of assignment
|
||||||
const AstNode* m_logicp = nullptr; // Current logic being built
|
const AstNode* m_logicp = nullptr; // Current logic being built
|
||||||
|
|
@ -2094,10 +2095,11 @@ class TristateVisitor final : public TristateBaseVisitor {
|
||||||
if (m_graphing) {
|
if (m_graphing) {
|
||||||
if (nodep->access().isWriteOrRW()) associateLogic(nodep, nodep->varp());
|
if (nodep->access().isWriteOrRW()) associateLogic(nodep, nodep->varp());
|
||||||
if (nodep->access().isReadOrRW()) associateLogic(nodep->varp(), nodep);
|
if (nodep->access().isReadOrRW()) associateLogic(nodep->varp(), nodep);
|
||||||
// A plain (non-Z) cross-hierarchy driver of an interface net that is tristate
|
// Only interface tristate nets need this: a plain (non-Z) cross-hierarchy
|
||||||
// must be treated as tristate here too, so it is collected as a contribution
|
// driver has no Z in its own module's graph, so mark the net tristate here to
|
||||||
// and combined with the interface's own drivers (otherwise it is silently
|
// collect the driver as a contribution. The ifaceTristate flag is only set for
|
||||||
// dropped and the net resolves using only the interface-internal Z driver).
|
// interface nets; a non-interface cross-module tri driver does nothing here and
|
||||||
|
// is instead rejected (E_UNSUPPORTED) later in insertTristates.
|
||||||
if (nodep->access().isWriteOrRW() && VN_IS(nodep, VarXRef)
|
if (nodep->access().isWriteOrRW() && VN_IS(nodep, VarXRef)
|
||||||
&& m_varAux(nodep->varp()).ifaceTristate) {
|
&& m_varAux(nodep->varp()).ifaceTristate) {
|
||||||
m_tgraph.setTristate(nodep->varp());
|
m_tgraph.setTristate(nodep->varp());
|
||||||
|
|
@ -2182,6 +2184,9 @@ class TristateVisitor final : public TristateBaseVisitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
void visit(AstNodeModule* nodep) override {
|
void visit(AstNodeModule* nodep) override {
|
||||||
|
// Interfaces are processed first in the constructor; skip the duplicate visit
|
||||||
|
// during the later iterateChildrenBackwardsConst() pass over all modules.
|
||||||
|
if (m_processedIfaces && VN_IS(nodep, Iface)) return;
|
||||||
UINFO(8, dbgState() << nodep);
|
UINFO(8, dbgState() << nodep);
|
||||||
VL_RESTORER(m_modp);
|
VL_RESTORER(m_modp);
|
||||||
VL_RESTORER(m_graphing);
|
VL_RESTORER(m_graphing);
|
||||||
|
|
@ -2318,24 +2323,19 @@ public:
|
||||||
// CONSTRUCTORS
|
// CONSTRUCTORS
|
||||||
explicit TristateVisitor(AstNetlist* netlistp) {
|
explicit TristateVisitor(AstNetlist* netlistp) {
|
||||||
m_tgraph.clearAndCheck();
|
m_tgraph.clearAndCheck();
|
||||||
// Process interface modules before any other module, so that interface tristate
|
// Process interface modules first, so an interface tristate net is recorded
|
||||||
// nets are recorded (AuxAstVar::ifaceTristate) before the modules that drive them
|
// (AuxAstVar::ifaceTristate) before any module that drives it across hierarchy is
|
||||||
// across hierarchy are processed. A module driving an interface tristate net with a
|
// graphed. A module driving such a net with a plain (non-Z) assign has no Z in its
|
||||||
// plain (non-Z) assign has no Z in its own graph to mark the net tristate, so without
|
// own graph to mark the net tristate, so without this its driver would be dropped.
|
||||||
// this ordering its driver would be silently dropped. Only modulesp() carries tristate
|
// Collect only the interfaces (reverse declaration order to match the pass below).
|
||||||
// logic (filesp/miscsp do not), so restricting the walk to it drops nothing versus the
|
std::vector<AstNodeModule*> ifacesp;
|
||||||
// historical iterateChildrenBackwardsConst(); iterate each group in reverse declaration
|
|
||||||
// order to match that order.
|
|
||||||
std::vector<AstNodeModule*> modps;
|
|
||||||
for (AstNode* modp = netlistp->modulesp(); modp; modp = modp->nextp()) {
|
for (AstNode* modp = netlistp->modulesp(); modp; modp = modp->nextp()) {
|
||||||
modps.push_back(VN_AS(modp, NodeModule));
|
if (VN_IS(modp, Iface)) ifacesp.push_back(VN_AS(modp, NodeModule));
|
||||||
}
|
|
||||||
for (auto it = modps.rbegin(); it != modps.rend(); ++it) {
|
|
||||||
if (VN_IS(*it, Iface)) iterate(*it);
|
|
||||||
}
|
|
||||||
for (auto it = modps.rbegin(); it != modps.rend(); ++it) {
|
|
||||||
if (!VN_IS(*it, Iface)) iterate(*it);
|
|
||||||
}
|
}
|
||||||
|
for (auto it = ifacesp.rbegin(); it != ifacesp.rend(); ++it) iterate(*it);
|
||||||
|
m_processedIfaces = true;
|
||||||
|
// Then the rest in the historical order; interfaces are skipped (already done).
|
||||||
|
iterateChildrenBackwardsConst(netlistp);
|
||||||
|
|
||||||
// Combine interface tristate contributions after all modules processed
|
// Combine interface tristate contributions after all modules processed
|
||||||
combineIfaceContribs();
|
combineIfaceContribs();
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,11 @@
|
||||||
// verilog_format: off
|
// verilog_format: off
|
||||||
`define stop $stop
|
`define stop $stop
|
||||||
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||||
|
`ifdef verilator
|
||||||
|
`define no_optimize(v) $c(v)
|
||||||
|
`else
|
||||||
|
`define no_optimize(v) (v)
|
||||||
|
`endif
|
||||||
// verilog_format: on
|
// verilog_format: on
|
||||||
|
|
||||||
// verilator lint_off MULTIDRIVEN
|
// verilator lint_off MULTIDRIVEN
|
||||||
|
|
@ -42,20 +47,23 @@ module t;
|
||||||
|
|
||||||
initial begin
|
initial begin
|
||||||
// dut drives the interface net through the inout port.
|
// dut drives the interface net through the inout port.
|
||||||
oe = 1;
|
oe = `no_optimize(1'b1);
|
||||||
val = 8'hA5;
|
val = `no_optimize(8'hA5);
|
||||||
top_oe = 0;
|
top_oe = `no_optimize(1'b0);
|
||||||
topval = 8'h00;
|
topval = `no_optimize(8'h00);
|
||||||
#1 `checkh(ifc0.data, 8'hA5);
|
#1;
|
||||||
|
`checkh(ifc0.data, 8'hA5);
|
||||||
// top drives, dut releases.
|
// top drives, dut releases.
|
||||||
oe = 0;
|
oe = `no_optimize(1'b0);
|
||||||
top_oe = 1;
|
top_oe = `no_optimize(1'b1);
|
||||||
topval = 8'h3C;
|
topval = `no_optimize(8'h3C);
|
||||||
#1 `checkh(ifc0.data, 8'h3C);
|
#1;
|
||||||
|
`checkh(ifc0.data, 8'h3C);
|
||||||
// Neither drives: net floats to Z.
|
// Neither drives: net floats to Z.
|
||||||
oe = 0;
|
oe = `no_optimize(1'b0);
|
||||||
top_oe = 0;
|
top_oe = `no_optimize(1'b0);
|
||||||
#1 `checkh(ifc0.data, 8'hzz);
|
#1;
|
||||||
|
`checkh(ifc0.data, 8'hzz);
|
||||||
$write("*-* All Finished *-*\n");
|
$write("*-* All Finished *-*\n");
|
||||||
$finish;
|
$finish;
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,11 @@
|
||||||
// verilog_format: off
|
// verilog_format: off
|
||||||
`define stop $stop
|
`define stop $stop
|
||||||
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||||
|
`ifdef verilator
|
||||||
|
`define no_optimize(v) $c(v)
|
||||||
|
`else
|
||||||
|
`define no_optimize(v) (v)
|
||||||
|
`endif
|
||||||
// verilog_format: on
|
// verilog_format: on
|
||||||
|
|
||||||
// verilator lint_off MULTIDRIVEN
|
// verilator lint_off MULTIDRIVEN
|
||||||
|
|
@ -14,10 +19,12 @@
|
||||||
interface ifc;
|
interface ifc;
|
||||||
wire [1:0] w;
|
wire [1:0] w;
|
||||||
// Self-contained interface-internal tristate driver: 'z while en==0, so any
|
// Self-contained interface-internal tristate driver: 'z while en==0, so any
|
||||||
// external driver must win the net resolution.
|
// external driver must win the net resolution. no_optimize keeps the driver
|
||||||
bit en = 0;
|
// values from being constant-folded, so the resolution runs at run time.
|
||||||
|
logic en;
|
||||||
logic [1:0] wint;
|
logic [1:0] wint;
|
||||||
assign wint = 2'b11;
|
assign en = `no_optimize(1'b0);
|
||||||
|
assign wint = `no_optimize(2'b11);
|
||||||
assign w = en ? wint : 2'bzz;
|
assign w = en ? wint : 2'bzz;
|
||||||
// Read the resolved net from inside the interface (a consumer's view), so the
|
// Read the resolved net from inside the interface (a consumer's view), so the
|
||||||
// check sees the true resolution, not the driving module's own local copy.
|
// check sees the true resolution, not the driving module's own local copy.
|
||||||
|
|
@ -66,10 +73,10 @@ module t;
|
||||||
ifc_tri u_e ();
|
ifc_tri u_e ();
|
||||||
|
|
||||||
logic [1:0] va, vb, vc, ve;
|
logic [1:0] va, vb, vc, ve;
|
||||||
assign va = 2'b01;
|
assign va = `no_optimize(2'b01);
|
||||||
assign vb = 2'b10;
|
assign vb = `no_optimize(2'b10);
|
||||||
assign vc = 2'b11;
|
assign vc = `no_optimize(2'b11);
|
||||||
assign ve = 2'b01;
|
assign ve = `no_optimize(2'b01);
|
||||||
|
|
||||||
assign u_a.w = va; // plain top-level driver
|
assign u_a.w = va; // plain top-level driver
|
||||||
assign u_b.w = vb; // plain top-level driver
|
assign u_b.w = vb; // plain top-level driver
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue