mirror of
https://github.com/verilator/verilator.git
synced 2026-08-22 05:57:29 +02:00
Fix clock-gates with non-AND complex logic, bug220.
This commit is contained in:
@@ -11,6 +11,8 @@ indicates the contributor was also the author of the fix; Thanks!
|
|||||||
|
|
||||||
**** Skip SystemC tests if not installed. [Iztok Jeras]
|
**** Skip SystemC tests if not installed. [Iztok Jeras]
|
||||||
|
|
||||||
|
**** Fix clock-gates with non-AND complex logic, bug220. [Ashutosh Das]
|
||||||
|
|
||||||
**** Fix flushing VCD buffers on $stop. [Ashutosh Das]
|
**** Fix flushing VCD buffers on $stop. [Ashutosh Das]
|
||||||
|
|
||||||
**** Fix Mac OS-X compile issues, bug217. [Joshua Wise, Trevor Williams]
|
**** Fix Mac OS-X compile issues, bug217. [Joshua Wise, Trevor Williams]
|
||||||
|
|||||||
+2
-1
@@ -1629,7 +1629,8 @@ Used after a signal declaration to indicate the signal is used to gate a
|
|||||||
clock, and the user takes responsibility for insuring there are no races
|
clock, and the user takes responsibility for insuring there are no races
|
||||||
related to it. (Typically by adding a latch, and running static timing
|
related to it. (Typically by adding a latch, and running static timing
|
||||||
analysis.) This will cause the clock gate to be ignored in the scheduling
|
analysis.) This will cause the clock gate to be ignored in the scheduling
|
||||||
algorithm, improving performance.
|
algorithm, improving performance. It's also a good idea to enable the
|
||||||
|
IMPERFECTSCH warning, to insure all clock enables are properly recognized.
|
||||||
|
|
||||||
=item /*verilator coverage_block_off*/
|
=item /*verilator coverage_block_off*/
|
||||||
|
|
||||||
|
|||||||
+38
-15
@@ -941,28 +941,51 @@ void OrderVisitor::processBrokeLoop() {
|
|||||||
// Clock propagation
|
// Clock propagation
|
||||||
|
|
||||||
void OrderVisitor::processInputs() {
|
void OrderVisitor::processInputs() {
|
||||||
m_graph.userClearVertices(); // Vertex::user() // true if added as begin/end
|
m_graph.userClearVertices(); // Vertex::user() // true if processed
|
||||||
|
// Start at input vertex, process from input-to-output order
|
||||||
|
m_inputsVxp->isFromInput(true); // By definition
|
||||||
processInputsIterate(m_inputsVxp);
|
processInputsIterate(m_inputsVxp);
|
||||||
}
|
}
|
||||||
|
|
||||||
void OrderVisitor::processInputsIterate(OrderEitherVertex* vertexp) {
|
void OrderVisitor::processInputsIterate(OrderEitherVertex* vertexp) {
|
||||||
// Propagate PrimaryIn through simple assignments
|
// Propagate PrimaryIn through simple assignments
|
||||||
if (vertexp->user()) return; // Already processed
|
if (vertexp->user()) return; // Already processed
|
||||||
//UINFO(9," InIt "<<vertexp<<endl);
|
if (0 && debug()>=9) {
|
||||||
vertexp->user(true);
|
UINFO(9," InIt "<<vertexp<<endl);
|
||||||
if (OrderVarStdVertex* vvertexp = dynamic_cast<OrderVarStdVertex*>(vertexp)) {
|
if (OrderLogicVertex* vvertexp = dynamic_cast<OrderLogicVertex*>(vertexp)) {
|
||||||
vvertexp->isFromInput(true);
|
vvertexp->nodep()->dumpTree(cout,"- TT: ");
|
||||||
}
|
|
||||||
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
|
||||||
OrderEitherVertex* toVertexp = (OrderEitherVertex*)edgep->top();
|
|
||||||
if (OrderVarStdVertex* vvertexp = dynamic_cast<OrderVarStdVertex*>(toVertexp)) {
|
|
||||||
processInputsIterate(vvertexp);
|
|
||||||
}
|
}
|
||||||
if (OrderLogicVertex* vvertexp = dynamic_cast<OrderLogicVertex*>(toVertexp)) {
|
}
|
||||||
if (AstNodeAssign* nodep = vvertexp->nodep()->castNodeAssign()) {
|
vertexp->user(true); // Processing
|
||||||
if (nodep->lhsp()->castVarRef()
|
// First handle all inputs to this vertex, in most cases they'll be already processed earlier
|
||||||
&& nodep->rhsp()->castVarRef()) {
|
// Also, determine if this vertex is an input
|
||||||
UINFO(9," Input reassignment: "<<vvertexp<<endl);
|
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);
|
||||||
|
if (frVertexp->isFromInput()) {
|
||||||
|
if (inonly==1) inonly = 2;
|
||||||
|
} else if (dynamic_cast<OrderVarPostVertex*>(frVertexp)) {
|
||||||
|
// Ignore post assignments, just for ordering
|
||||||
|
} else {
|
||||||
|
//UINFO(9," InItStopDueTo "<<frVertexp<<endl);
|
||||||
|
inonly = 0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (inonly == 2) { // Set it. Note may have already been set earlier, too
|
||||||
|
UINFO(9," Input reassignment: "<<vertexp<<endl);
|
||||||
|
vertexp->isFromInput(true);
|
||||||
|
}
|
||||||
|
// If we're still an input, process all targets of this vertex
|
||||||
|
if (vertexp->isFromInput()) {
|
||||||
|
for (V3GraphEdge* edgep = vertexp->outBeginp(); edgep; edgep=edgep->outNextp()) {
|
||||||
|
OrderEitherVertex* toVertexp = (OrderEitherVertex*)edgep->top();
|
||||||
|
if (OrderVarStdVertex* vvertexp = dynamic_cast<OrderVarStdVertex*>(toVertexp)) {
|
||||||
|
processInputsIterate(vvertexp);
|
||||||
|
}
|
||||||
|
if (OrderLogicVertex* vvertexp = dynamic_cast<OrderLogicVertex*>(toVertexp)) {
|
||||||
|
if (vvertexp->nodep()->castNodeAssign()) {
|
||||||
processInputsIterate(vvertexp);
|
processInputsIterate(vvertexp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-6
@@ -132,10 +132,11 @@ class OrderEitherVertex : public V3GraphVertex {
|
|||||||
AstScope* m_scopep; // Scope the vertex is in
|
AstScope* m_scopep; // Scope the vertex is in
|
||||||
AstSenTree* m_domainp; // Clock domain (NULL = to be computed as we iterate)
|
AstSenTree* m_domainp; // Clock domain (NULL = to be computed as we iterate)
|
||||||
OrderLoopId m_inLoop; // Loop number vertex is in
|
OrderLoopId m_inLoop; // Loop number vertex is in
|
||||||
|
bool m_isFromInput; // From input, or derrived therefrom (conservatively false)
|
||||||
public:
|
public:
|
||||||
OrderEitherVertex(V3Graph* graphp, AstScope* scopep, AstSenTree* domainp)
|
OrderEitherVertex(V3Graph* graphp, AstScope* scopep, AstSenTree* domainp)
|
||||||
: V3GraphVertex(graphp), m_scopep(scopep), m_domainp(domainp)
|
: V3GraphVertex(graphp), m_scopep(scopep), m_domainp(domainp)
|
||||||
, m_inLoop(LOOPID_UNKNOWN) {
|
, m_inLoop(LOOPID_UNKNOWN), m_isFromInput(false) {
|
||||||
}
|
}
|
||||||
virtual ~OrderEitherVertex() {}
|
virtual ~OrderEitherVertex() {}
|
||||||
// Methods
|
// Methods
|
||||||
@@ -148,12 +149,16 @@ public:
|
|||||||
AstSenTree* domainp() const { return m_domainp; }
|
AstSenTree* domainp() const { return m_domainp; }
|
||||||
OrderLoopId inLoop() const { return m_inLoop; }
|
OrderLoopId inLoop() const { return m_inLoop; }
|
||||||
void inLoop(OrderLoopId inloop) { m_inLoop = inloop; }
|
void inLoop(OrderLoopId inloop) { m_inLoop = inloop; }
|
||||||
|
void isFromInput(bool flag) { m_isFromInput=flag; }
|
||||||
|
bool isFromInput() const { return m_isFromInput; }
|
||||||
};
|
};
|
||||||
|
|
||||||
class OrderInputsVertex : public OrderEitherVertex {
|
class OrderInputsVertex : public OrderEitherVertex {
|
||||||
public:
|
public:
|
||||||
OrderInputsVertex(V3Graph* graphp, AstSenTree* domainp)
|
OrderInputsVertex(V3Graph* graphp, AstSenTree* domainp)
|
||||||
: OrderEitherVertex(graphp, NULL, domainp) {}
|
: OrderEitherVertex(graphp, NULL, domainp) {
|
||||||
|
isFromInput(true); // By definition
|
||||||
|
}
|
||||||
virtual ~OrderInputsVertex() {}
|
virtual ~OrderInputsVertex() {}
|
||||||
virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_INPUTS; }
|
virtual OrderVEdgeType type() const { return OrderVEdgeType::VERTEX_INPUTS; }
|
||||||
virtual string name() const { return "*INPUTS*"; }
|
virtual string name() const { return "*INPUTS*"; }
|
||||||
@@ -195,11 +200,10 @@ class OrderVarVertex : public OrderEitherVertex {
|
|||||||
AstVarScope* m_varScp;
|
AstVarScope* m_varScp;
|
||||||
OrderVarVertex* m_pilNewVertexp; // for processInsLoopNewVar
|
OrderVarVertex* m_pilNewVertexp; // for processInsLoopNewVar
|
||||||
bool m_isClock; // Used as clock
|
bool m_isClock; // Used as clock
|
||||||
bool m_isFromInput; // From input, or derrived therefrom (conservatively false)
|
|
||||||
public:
|
public:
|
||||||
OrderVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
|
OrderVarVertex(V3Graph* graphp, AstScope* scopep, AstVarScope* varScp)
|
||||||
: OrderEitherVertex(graphp, scopep, NULL), m_varScp(varScp)
|
: OrderEitherVertex(graphp, scopep, NULL), m_varScp(varScp)
|
||||||
, m_pilNewVertexp(NULL), m_isClock(false), m_isFromInput(false)
|
, m_pilNewVertexp(NULL), m_isClock(false)
|
||||||
{}
|
{}
|
||||||
virtual ~OrderVarVertex() {}
|
virtual ~OrderVarVertex() {}
|
||||||
virtual OrderVarVertex* clone (V3Graph* graphp) const = 0;
|
virtual OrderVarVertex* clone (V3Graph* graphp) const = 0;
|
||||||
@@ -208,8 +212,6 @@ public:
|
|||||||
AstVarScope* varScp() const { return m_varScp; }
|
AstVarScope* varScp() const { return m_varScp; }
|
||||||
void isClock(bool flag) { m_isClock=flag; }
|
void isClock(bool flag) { m_isClock=flag; }
|
||||||
bool isClock() const { return m_isClock; }
|
bool isClock() const { return m_isClock; }
|
||||||
void isFromInput(bool flag) { m_isFromInput=flag; }
|
|
||||||
bool isFromInput() const { return m_isFromInput; }
|
|
||||||
OrderVarVertex* pilNewVertexp() const { return m_pilNewVertexp; }
|
OrderVarVertex* pilNewVertexp() const { return m_pilNewVertexp; }
|
||||||
void pilNewVertexp (OrderVarVertex* vertexp) { m_pilNewVertexp = vertexp; }
|
void pilNewVertexp (OrderVarVertex* vertexp) { m_pilNewVertexp = vertexp; }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ sub parameter {
|
|||||||
elsif ($param =~ /\.pl/) {
|
elsif ($param =~ /\.pl/) {
|
||||||
push @opt_tests, $param;
|
push @opt_tests, $param;
|
||||||
}
|
}
|
||||||
elsif ($param =~ /^--debugi/) {
|
elsif ($param =~ /^-?-debugi/) {
|
||||||
push @Opt_Driver_Verilator_Flags, $param;
|
push @Opt_Driver_Verilator_Flags, $param;
|
||||||
$_Parameter_Next_Level = $param;
|
$_Parameter_Next_Level = $param;
|
||||||
}
|
}
|
||||||
|
|||||||
Executable
+18
@@ -0,0 +1,18 @@
|
|||||||
|
#!/usr/bin/perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2003 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.
|
||||||
|
|
||||||
|
compile (
|
||||||
|
);
|
||||||
|
|
||||||
|
execute (
|
||||||
|
check_finished=>1,
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed into the Public Domain, for any use,
|
||||||
|
// without warranty, 2010 by Wilson Snyder.
|
||||||
|
//
|
||||||
|
// --------------------------------------------------------
|
||||||
|
// Bug Description:
|
||||||
|
//
|
||||||
|
// Issue: The gated clock gclk_vld[0] toggles but dvld[0]
|
||||||
|
// input to the flop does not propagate to the output
|
||||||
|
// signal entry_vld[0] correctly. The value that propagates
|
||||||
|
// is the new value of dvld[0] not the one just before the
|
||||||
|
// posedge of gclk_vld[0].
|
||||||
|
// --------------------------------------------------------
|
||||||
|
|
||||||
|
// Define to see the bug with test failing with gated clock 'gclk_vld'
|
||||||
|
// Comment out the define to see the test passing with ungated clock 'clk'
|
||||||
|
`define GATED_CLK_TESTCASE 1
|
||||||
|
|
||||||
|
// A side effect of the problem is this warning, disabled by default
|
||||||
|
//verilator lint_on IMPERFECTSCH
|
||||||
|
|
||||||
|
// Test Bench
|
||||||
|
module t (/*AUTOARG*/
|
||||||
|
// Inputs
|
||||||
|
clk
|
||||||
|
);
|
||||||
|
input clk;
|
||||||
|
|
||||||
|
integer cyc=0;
|
||||||
|
reg [63:0] crc;
|
||||||
|
|
||||||
|
// Take CRC data and apply to testblock inputs
|
||||||
|
wire [7:0] dvld = crc[7:0];
|
||||||
|
wire [7:0] ff_en_e1 = crc[15:8];
|
||||||
|
|
||||||
|
/*AUTOWIRE*/
|
||||||
|
// Beginning of automatic wires (for undeclared instantiated-module outputs)
|
||||||
|
wire [7:0] entry_vld; // From test of Test.v
|
||||||
|
wire [7:0] ff_en_vld; // From test of Test.v
|
||||||
|
// End of automatics
|
||||||
|
|
||||||
|
Test test (/*AUTOINST*/
|
||||||
|
// Outputs
|
||||||
|
.ff_en_vld (ff_en_vld[7:0]),
|
||||||
|
.entry_vld (entry_vld[7:0]),
|
||||||
|
// Inputs
|
||||||
|
.clk (clk),
|
||||||
|
.dvld (dvld[7:0]),
|
||||||
|
.ff_en_e1 (ff_en_e1[7:0]));
|
||||||
|
|
||||||
|
reg err_code;
|
||||||
|
reg ffq_clk_active;
|
||||||
|
reg [7:0] prv_dvld;
|
||||||
|
|
||||||
|
initial begin
|
||||||
|
err_code = 0;
|
||||||
|
ffq_clk_active = 0;
|
||||||
|
end
|
||||||
|
always @ (posedge clk) begin
|
||||||
|
prv_dvld = test.dvld;
|
||||||
|
end
|
||||||
|
always @ (negedge test.ff_entry_dvld_0.clk) begin
|
||||||
|
ffq_clk_active = 1;
|
||||||
|
if (test.entry_vld[0] !== prv_dvld[0]) err_code = 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Test loop
|
||||||
|
always @ (posedge clk) begin
|
||||||
|
`ifdef TEST_VERBOSE
|
||||||
|
$write("[%0t] cyc==%0d crc=%x ",$time, cyc, crc);
|
||||||
|
$display(" en=%b fen=%b d=%b ev=%b",
|
||||||
|
test.flop_en_vld[0], test.ff_en_vld[0],
|
||||||
|
test.dvld[0], test.entry_vld[0]);
|
||||||
|
`endif
|
||||||
|
cyc <= cyc + 1;
|
||||||
|
crc <= {crc[62:0], crc[63]^crc[2]^crc[0]};
|
||||||
|
if (cyc<3) begin
|
||||||
|
crc <= 64'h5aef0c8d_d70a4497;
|
||||||
|
end
|
||||||
|
else if (cyc==99) begin
|
||||||
|
$write("[%0t] cyc==%0d crc=%x\n",$time, cyc, crc);
|
||||||
|
if (ffq_clk_active == 0) begin
|
||||||
|
$display ("----");
|
||||||
|
$display ("%%Error: TESTCASE FAILED with no Clock arriving at FFQs");
|
||||||
|
$display ("----");
|
||||||
|
$stop;
|
||||||
|
end
|
||||||
|
else if (err_code) begin
|
||||||
|
$display ("----");
|
||||||
|
$display ("%%Error: TESTCASE FAILED with invalid propagation of 'd' to 'q' of FFQs");
|
||||||
|
$display ("----");
|
||||||
|
$stop;
|
||||||
|
end
|
||||||
|
else begin
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module llq (clk, d, q);
|
||||||
|
parameter WIDTH = 32;
|
||||||
|
input clk;
|
||||||
|
input [WIDTH-1:0] d;
|
||||||
|
output [WIDTH-1:0] q;
|
||||||
|
|
||||||
|
reg [WIDTH-1:0] qr;
|
||||||
|
|
||||||
|
/* verilator lint_off COMBDLY */
|
||||||
|
|
||||||
|
always @(clk or d)
|
||||||
|
if (clk == 1'b0)
|
||||||
|
qr <= d;
|
||||||
|
|
||||||
|
/* verilator lint_on COMBDLY */
|
||||||
|
|
||||||
|
assign q = qr;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
module ffq (clk, d, q);
|
||||||
|
parameter WIDTH = 32;
|
||||||
|
input clk;
|
||||||
|
input [WIDTH-1:0] d;
|
||||||
|
output [WIDTH-1:0] q;
|
||||||
|
|
||||||
|
reg [WIDTH-1:0] qr;
|
||||||
|
|
||||||
|
always @(posedge clk)
|
||||||
|
qr <= d;
|
||||||
|
|
||||||
|
assign q = qr;
|
||||||
|
endmodule
|
||||||
|
|
||||||
|
// DUT module
|
||||||
|
module Test (/*AUTOARG*/
|
||||||
|
// Outputs
|
||||||
|
ff_en_vld, entry_vld,
|
||||||
|
// Inputs
|
||||||
|
clk, dvld, ff_en_e1
|
||||||
|
);
|
||||||
|
input clk;
|
||||||
|
|
||||||
|
input [7:0] dvld;
|
||||||
|
input [7:0] ff_en_e1;
|
||||||
|
|
||||||
|
output [7:0] ff_en_vld;
|
||||||
|
output wire [7:0] entry_vld;
|
||||||
|
|
||||||
|
wire [7:0] gclk_vld;
|
||||||
|
wire [7:0] ff_en_vld /*verilator clock_enable*/;
|
||||||
|
reg [7:0] flop_en_vld;
|
||||||
|
|
||||||
|
always @(posedge clk) flop_en_vld <= ff_en_e1;
|
||||||
|
|
||||||
|
// clock gating
|
||||||
|
`ifdef GATED_CLK_TESTCASE
|
||||||
|
assign gclk_vld = {8{clk}} & ff_en_vld;
|
||||||
|
`else
|
||||||
|
assign gclk_vld = {8{clk}};
|
||||||
|
`endif
|
||||||
|
|
||||||
|
// latch for avoiding glitch on the clock gating control
|
||||||
|
llq #(8) dp_ff_en_vld (.clk(clk), .d(flop_en_vld), .q(ff_en_vld));
|
||||||
|
|
||||||
|
// flops that use the gated clock signal
|
||||||
|
ffq #(1) ff_entry_dvld_0 (.clk(gclk_vld[0]), .d(dvld[0]), .q(entry_vld[0]));
|
||||||
|
ffq #(1) ff_entry_dvld_1 (.clk(gclk_vld[1]), .d(dvld[1]), .q(entry_vld[1]));
|
||||||
|
ffq #(1) ff_entry_dvld_2 (.clk(gclk_vld[2]), .d(dvld[2]), .q(entry_vld[2]));
|
||||||
|
ffq #(1) ff_entry_dvld_3 (.clk(gclk_vld[3]), .d(dvld[3]), .q(entry_vld[3]));
|
||||||
|
ffq #(1) ff_entry_dvld_4 (.clk(gclk_vld[4]), .d(dvld[4]), .q(entry_vld[4]));
|
||||||
|
ffq #(1) ff_entry_dvld_5 (.clk(gclk_vld[5]), .d(dvld[5]), .q(entry_vld[5]));
|
||||||
|
ffq #(1) ff_entry_dvld_6 (.clk(gclk_vld[6]), .d(dvld[6]), .q(entry_vld[6]));
|
||||||
|
ffq #(1) ff_entry_dvld_7 (.clk(gclk_vld[7]), .d(dvld[7]), .q(entry_vld[7]));
|
||||||
|
|
||||||
|
endmodule
|
||||||
Reference in New Issue
Block a user