diff --git a/Changes b/Changes index e2b4d0440..17b2f6fc8 100644 --- a/Changes +++ b/Changes @@ -9,6 +9,8 @@ indicates the contributor was also the author of the fix; Thanks! *** Add --pins-sc-uint and --pins-sc-biguint, bug638. [Alex Hornung] +**** Fix simulation error when inputs and MULTIDRIVEN, bug634. [Ted Campbell] + **** Fix module resolution with __, bug631. [Jason McMullan] diff --git a/src/V3Order.cpp b/src/V3Order.cpp index e204c9f39..dd9755001 100644 --- a/src/V3Order.cpp +++ b/src/V3Order.cpp @@ -375,8 +375,10 @@ private: void processBrokeLoop(); #endif void processCircular(); + typedef deque VertexVec; void processInputs(); - void processInputsIterate(OrderEitherVertex* vertexp); + void processInputsInIterate(OrderEitherVertex* vertexp, VertexVec& todoVec); + void processInputsOutIterate(OrderEitherVertex* vertexp, VertexVec& todoVec); void processSensitive(); void processDomains(); void processDomainsIterate(OrderEitherVertex* vertexp); @@ -1059,28 +1061,33 @@ void OrderVisitor::processBrokeLoop() { // Clock propagation void OrderVisitor::processInputs() { - m_graph.userClearVertices(); // Vertex::user() // true if processed + m_graph.userClearVertices(); // Vertex::user() // 1 if input recursed, 2 if marked as input, 3 if out-edges recursed // Start at input vertex, process from input-to-output order + VertexVec todoVec; // List of newly-input marked vectors we need to process + todoVec.push_front(m_inputsVxp); m_inputsVxp->isFromInput(true); // By definition - processInputsIterate(m_inputsVxp); + while (!todoVec.empty()) { + OrderEitherVertex* vertexp = todoVec.back(); todoVec.pop_back(); + processInputsOutIterate(vertexp, todoVec); + } } -void OrderVisitor::processInputsIterate(OrderEitherVertex* vertexp) { +void OrderVisitor::processInputsInIterate(OrderEitherVertex* vertexp, VertexVec& todoVec) { // Propagate PrimaryIn through simple assignments if (vertexp->user()) return; // Already processed if (0 && debug()>=9) { - UINFO(9," InIt "<(vertexp)) { vvertexp->nodep()->dumpTree(cout,"- TT: "); } } - vertexp->user(true); // Processing + vertexp->user(1); // Processing // First handle all inputs to this vertex, in most cases they'll be already processed earlier // Also, determine if this vertex is an input int inonly = 1; // 0=no, 1=maybe, 2=yes until a no for (V3GraphEdge* edgep = vertexp->inBeginp(); edgep; edgep=edgep->inNextp()) { OrderEitherVertex* frVertexp = (OrderEitherVertex*)edgep->fromp(); - processInputsIterate(frVertexp); + processInputsInIterate(frVertexp, todoVec); if (frVertexp->isFromInput()) { if (inonly==1) inonly = 2; } else if (dynamic_cast(frVertexp)) { @@ -1091,20 +1098,38 @@ void OrderVisitor::processInputsIterate(OrderEitherVertex* vertexp) { break; } } - if (inonly == 2) { // Set it. Note may have already been set earlier, too + + if (inonly == 2 && vertexp->user()<2) { // Set it. Note may have already been set earlier, too UINFO(9," Input reassignment: "<isFromInput(true); + vertexp->user(2); // 2 means on list + // Can't work on out-edges of a node we know is an input immediately, + // as it might visit other nodes before their input state is resolved. + // So push to list and work on it later when all in-edges known resolved + todoVec.push_back(vertexp); } - // If we're still an input, process all targets of this vertex - if (vertexp->isFromInput()) { + //UINFO(9," InIdone "<user()==3) return; // Already out processed + //UINFO(9," InOIter "<isFromInput()) v3fatalSrc("processInputsOutIterate only for input marked vertexes"); + vertexp->user(3); // out-edges processed + + { + // Propagate PrimaryIn through simple assignments, followint target of vertex for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) { OrderEitherVertex* toVertexp = (OrderEitherVertex*)edgep->top(); if (OrderVarStdVertex* vvertexp = dynamic_cast(toVertexp)) { - processInputsIterate(vvertexp); + processInputsInIterate(vvertexp, todoVec); } if (OrderLogicVertex* vvertexp = dynamic_cast(toVertexp)) { if (vvertexp->nodep()->castNodeAssign()) { - processInputsIterate(vvertexp); + processInputsInIterate(vvertexp, todoVec); } } } diff --git a/test_regress/t/t_order_multidriven.cpp b/test_regress/t/t_order_multidriven.cpp new file mode 100644 index 000000000..86068112d --- /dev/null +++ b/test_regress/t/t_order_multidriven.cpp @@ -0,0 +1,56 @@ +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2013 by Ted Campbell. + +#include "Vt_order_multidriven.h" +#include "verilated.h" +#include "verilated_vcd_c.h" + +Vt_order_multidriven * vcore; +VerilatedVcdC * vcd; +vluint64_t vtime; + +#define PHASE_90 + +static void half_cycle( int clk ) { + if ( clk & 1 ) vcore->i_clk_wr = !vcore->i_clk_wr; + if ( clk & 2 ) vcore->i_clk_rd = !vcore->i_clk_rd; + + vtime += 10 / 2; + + vcore->eval(); + vcore->eval(); + + vcd->dump( vtime ); +} + +static void cycle() { +#ifdef PHASE_90 + half_cycle( 1 ); + half_cycle( 2 ); + half_cycle( 1 ); + half_cycle( 2 ); +#else + half_cycle( 3 ); + half_cycle( 3 ); +#endif +} + +int main() { + + Verilated::traceEverOn( true ); + + vcore = new Vt_order_multidriven; + vcd = new VerilatedVcdC; + + vcore->trace( vcd, 99 ); + vcd->open( "obj_dir/t_order_multidriven/sim.vcd" ); + + vcore->i_clk_wr = 0; + vcore->i_clk_rd = 0; + + for ( int i = 0; i < 256; ++i ) + cycle( ); + + vcd->close( ); + printf("*-* All Finished *-*\n"); +} diff --git a/test_regress/t/t_order_multidriven.pl b/test_regress/t/t_order_multidriven.pl new file mode 100755 index 000000000..1edafb042 --- /dev/null +++ b/test_regress/t/t_order_multidriven.pl @@ -0,0 +1,23 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003-2013 by Wilson Snyder. 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. + +$Self->{vlt} or $Self->skip("Verilator only test"); + +compile ( + make_top_shell => 0, + make_main => 0, + v_flags2 => ["--trace --exe $Self->{t_dir}/t_order_multidriven.cpp"], + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_order_multidriven.v b/test_regress/t/t_order_multidriven.v new file mode 100644 index 000000000..1a2ff5961 --- /dev/null +++ b/test_regress/t/t_order_multidriven.v @@ -0,0 +1,192 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2013 by Ted Campbell. + +//With MULTI_CLK defined shows bug, without it is hidden +`define MULTI_CLK + +//bug634 + +module t ( + input i_clk_wr, + input i_clk_rd + ); + + wire wr$wen; + wire [7:0] wr$addr; + wire [7:0] wr$wdata; + wire [7:0] wr$rdata; + + wire rd$wen; + wire [7:0] rd$addr; + wire [7:0] rd$wdata; + wire [7:0] rd$rdata; + + wire clk_wr; + wire clk_rd; + + `ifdef MULTI_CLK + assign clk_wr = i_clk_wr; + assign clk_rd = i_clk_rd; + `else + assign clk_wr = i_clk_wr; + assign clk_rd = i_clk_wr; + `endif + + FooWr u_wr ( + .i_clk ( clk_wr ), + + .o_wen ( wr$wen ), + .o_addr ( wr$addr ), + .o_wdata ( wr$wdata ), + .i_rdata ( wr$rdata ) + ); + + FooRd u_rd ( + .i_clk ( clk_rd ), + + .o_wen ( rd$wen ), + .o_addr ( rd$addr ), + .o_wdata ( rd$wdata ), + .i_rdata ( rd$rdata ) + ); + + FooMem u_mem ( + .iv_clk ( {clk_wr, clk_rd } ), + .iv_wen ( {wr$wen, rd$wen } ), + .iv_addr ( {wr$addr, rd$addr } ), + .iv_wdata ( {wr$wdata,rd$wdata} ), + .ov_rdata ( {wr$rdata,rd$rdata} ) + ); + +endmodule + + +// Memory Writer +module FooWr( + input i_clk, + + output o_wen, + output [7:0] o_addr, + output [7:0] o_wdata, + input [7:0] i_rdata + ); + + reg [7:0] cnt = 0; + + // Count [0,200] + always @( posedge i_clk ) + if ( cnt < 8'd50 ) + cnt <= cnt + 8'd1; + + // Write addr in (10,30) if even + assign o_wen = ( cnt > 8'd10 ) && ( cnt < 8'd30 ) && ( cnt[0] == 1'b0 ); + assign o_addr = cnt; + assign o_wdata = cnt; + +endmodule + + +// Memory Reader +module FooRd( + input i_clk, + + output o_wen, + output [7:0] o_addr, + output [7:0] o_wdata, + input [7:0] i_rdata + ); + + reg [7:0] cnt = 0; + reg [7:0] addr_r; + reg en_r; + + // Count [0,200] + always @( posedge i_clk ) + if ( cnt < 8'd200 ) + cnt <= cnt + 8'd1; + + // Read data + assign o_wen = 0; + assign o_addr = cnt - 8'd100; + + // Track issued read + always @( posedge i_clk ) + begin + addr_r <= o_addr; + en_r <= ( cnt > 8'd110 ) && ( cnt < 8'd130 ) && ( cnt[0] == 1'b0 ); + end + + // Display to console 100 cycles after writer + always @( negedge i_clk ) + if ( en_r ) begin +`ifdef TEST_VERBOSE + $display( "MEM[%x] == %x", addr_r, i_rdata ); +`endif + if (addr_r != i_rdata) $stop; + end + +endmodule + + +// Multi-port memory abstraction +module FooMem( + input [2 -1:0] iv_clk, + input [2 -1:0] iv_wen, + input [2*8-1:0] iv_addr, + input [2*8-1:0] iv_wdata, + output [2*8-1:0] ov_rdata + ); + + FooMemImpl u_impl ( + .a_clk ( iv_clk [0*1+:1] ), + .a_wen ( iv_wen [0*1+:1] ), + .a_addr ( iv_addr [0*8+:8] ), + .a_wdata ( iv_wdata[0*8+:8] ), + .a_rdata ( ov_rdata[0*8+:8] ), + + .b_clk ( iv_clk [1*1+:1] ), + .b_wen ( iv_wen [1*1+:1] ), + .b_addr ( iv_addr [1*8+:8] ), + .b_wdata ( iv_wdata[1*8+:8] ), + .b_rdata ( ov_rdata[1*8+:8] ) + ); + +endmodule + + +// Dual-Port L1 Memory Implementation +module FooMemImpl( + input a_clk, + input a_wen, + input [7:0] a_addr, + input [7:0] a_wdata, + output [7:0] a_rdata, + + input b_clk, + input b_wen, + input [7:0] b_addr, + input [7:0] b_wdata, + output [7:0] b_rdata + ); + + /* verilator lint_off MULTIDRIVEN */ + reg [7:0] mem[0:255]; + /* verilator lint_on MULTIDRIVEN */ + + always @( posedge a_clk ) + if ( a_wen ) + mem[a_addr] <= a_wdata; + + always @( posedge b_clk ) + if ( b_wen ) + mem[b_addr] <= b_wdata; + + always @( posedge a_clk ) + a_rdata <= mem[a_addr]; + + always @( posedge b_clk ) + b_rdata <= mem[b_addr]; + +endmodule