From d20f22beb10110d7078144ede9f00e4b822ad0d4 Mon Sep 17 00:00:00 2001 From: Yutetsu TAKATSUKASA Date: Sun, 7 Aug 2022 21:12:57 +0900 Subject: [PATCH] Fix tristate logic when reading inout port in a module #3399 (#3523) * Tests: Add a test to reproduce #3399 * Fix #3399. When reading an inout port in a module, it should refer the original inout port, not the generated MODTEMP. --- src/V3Tristate.cpp | 10 +++- test_regress/t/t_tri_inout.cpp | 10 ++++ test_regress/t/t_tri_inout.v | 85 +++++++++++++++++++++++++++++++++- 3 files changed, 103 insertions(+), 2 deletions(-) diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index 6f80c2f82..20861faa9 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -385,7 +385,15 @@ class TristateVisitor final : public TristateBaseVisitor { return newp; } AstNode* getEnp(AstNode* nodep) { - if (!nodep->user1p()) { + if (nodep->user1p()) { + if (AstVarRef* const refp = VN_CAST(nodep, VarRef)) { + if (refp->varp()->isIO()) { + // When reading a tri-state port, we can always use the value + // because such port will have resolution logic in upper module. + return newAllZerosOrOnes(nodep, true); + } + } + } else { // There's no select being built yet, so add what will become a // constant output enable driver of all 1's nodep->user1p(newAllZerosOrOnes(nodep, true)); diff --git a/test_regress/t/t_tri_inout.cpp b/test_regress/t/t_tri_inout.cpp index b3bd8aa46..874c5f15b 100644 --- a/test_regress/t/t_tri_inout.cpp +++ b/test_regress/t/t_tri_inout.cpp @@ -47,6 +47,16 @@ int main() { } } } + tb->SEL = tb->A = tb->B = 0; + + for (int i = 0; i < 256; ++i) { + tb->clk = 0; + tb->eval(); + tb->clk = 1; + tb->eval(); + if (tb->done) break; + if (i + 1 == 256) pass = false; + } if (pass) { VL_PRINTF("*-* All Finished *-*\n"); diff --git a/test_regress/t/t_tri_inout.v b/test_regress/t/t_tri_inout.v index d6624b04d..d9673beb9 100644 --- a/test_regress/t/t_tri_inout.v +++ b/test_regress/t/t_tri_inout.v @@ -4,10 +4,12 @@ // without warranty, 2008 by Lane Brooks. // SPDX-License-Identifier: CC0-1.0 -module top (input A, input B, input SEL, output Y1, output Y2, output Z); +module top (input A, input B, input SEL, input clk, output Y1, output Y2, output Z, output done); io io1(.A(A), .OE( SEL), .Z(Z), .Y(Y1)); pass io2(.A(B), .OE(!SEL), .Z(Z), .Y(Y2)); assign Z = 1'bz; + + pad_checker u_pad_checker(.clk(clk), .done(done)); endmodule module pass (input A, input OE, inout Z, output Y); @@ -27,3 +29,84 @@ module io_noinline (input A, input OE, inout Z, output Y); assign Y = Z; assign Z = 1'bz; endmodule + + +module pad_checker(input wire clk, output wire done); + wire tri_pad; + reg [1:0] ie = '0; + reg [1:0] oe = '0; + reg [1:0] in = '0; + wire out_0, out_1; + + pad u_pad0(.pad(tri_pad), .ie(ie[0]), .oe(oe[0]), .to_pad(in[0]), .from_pad(out_0)); + pad u_pad1(.pad(tri_pad), .ie(ie[1]), .oe(oe[1]), .to_pad(in[1]), .from_pad(out_1)); + + wire bin_pad_in_0, bin_pad_in_1; + wire bin_pad_01, bin_pad_10; + wire bin_pad_en_01, bin_pad_en_10; + wire bin_from_pad_out_0, bin_from_pad_out_1; + wire bin_from_pad_en_0, bin_from_pad_en_1; + + // Expectation model that simulates how Verilator solves tri-state + pad_binary u_pad_bin_0(.pad_in(bin_pad_in_0), + .pad_out(bin_pad_01), + .pad_en(bin_pad_en_01), + .ie(ie[0]), .oe(oe[0]), + .to_pad(in[0]), + .from_pad_out(bin_from_pad_out_0), + .from_pad_en(bin_from_pad_en_0)); + + pad_binary u_pad_bin_1(.pad_in(bin_pad_in_1), + .pad_out(bin_pad_10), + .pad_en(bin_pad_en_10), + .ie(ie[1]), + .oe(oe[1]), + .to_pad(in[1]), + .from_pad_out(bin_from_pad_out_1), + .from_pad_en(bin_from_pad_en_1)); + + assign bin_pad_in_0 = (bin_pad_en_10 & bin_pad_10) | (bin_pad_en_01 & bin_pad_01); + assign bin_pad_in_1 = (bin_pad_en_01 & bin_pad_01) | (bin_pad_en_10 & bin_pad_10); + + + logic done_reg = 0; + assign done = done_reg; + always @(posedge clk) begin + if ({ie, oe, in} == 6'b111111) begin + done_reg <= 1'b1; + end else begin + if (out_0 != bin_from_pad_out_0) begin + $display("ie:%b oe:%b in:%b out0 act:%b exp:%b", ie[0], oe[0], in[0], out_0, bin_from_pad_out_0); + $stop; + end + if (out_1 != bin_from_pad_out_1) begin + $display("ie:%b oe:%b in:%b out1 act:%b exp:%b", ie[1], oe[1], in[1], out_1, bin_from_pad_out_1); + $stop; + end + // Let's try all combination + {ie, oe, in} <= {ie, oe, in} + 1; + end + end + +endmodule + +module pad(inout wire pad, input wire ie, input wire oe, input wire to_pad, output wire from_pad); + + assign pad = oe ? to_pad : 1'bz; + assign from_pad = ie ? pad : 1'bz; +endmodule + +module pad_binary(input wire pad_in, + output wire pad_out, + output wire pad_en, + input wire ie, + input wire oe, + input wire to_pad, + output from_pad_out, + output wire from_pad_en); + + assign pad_out = oe & to_pad; + assign pad_en = oe; + assign from_pad_out = ie & ((oe & to_pad) | pad_in); + assign from_pad_en = ie; +endmodule