Fix memory delayed assignments from multiple clock domains.

This commit is contained in:
Wilson Snyder 2012-01-26 08:10:50 -05:00
parent 717f45d117
commit af9e85bda1
6 changed files with 261 additions and 38 deletions

View File

@ -6,6 +6,10 @@ indicates the contributor was also the author of the fix; Thanks!
* Verilator 3.832 devel * Verilator 3.832 devel
*** Fix memory delayed assignments from multiple clock domains. [Andrew Ling]
*** Report MULTIDRIVEN on memories set in multiple clock domains.
* Verilator 3.831 2012/01/20 * Verilator 3.831 2012/01/20

View File

@ -74,14 +74,15 @@ private:
// NODE STATE // NODE STATE
// Cleared each module: // Cleared each module:
// AstVarScope::user1p() -> AstVarScope*. Points to temp var created. // AstVarScope::user1p() -> AstVarScope*. Points to temp var created.
// AstVarScope::user2p() -> AstActive*. Points to activity block of signal // AstVarScope::user2p() -> AstActive*. Points to activity block of signal (valid when AstVarScope::user1p is valid)
// AstVarScope::user4p() -> AstAlwaysPost*. Post block for this variable // AstVarScope::user4p() -> AstAlwaysPost*. Post block for this variable
// AstVarScope::user5() -> VarUsage. Tracks delayed vs non-delayed usage // AstVarScope::user5() -> VarUsage. Tracks delayed vs non-delayed usage
// AstVar::user2() -> bool. Set true if already made warning // AstVar::user2() -> bool. Set true if already made warning
// AstVar::user4() -> int. Vector number, for assignment creation // AstVar::user4() -> int. Vector number, for assignment creation
// AstVarRef::user2() -> bool. Set true if already processed // AstVarRef::user2() -> bool. Set true if already processed
// AstAlwaysPost::user2() -> ActActive*. Points to activity block of signal (valid when AstAlwaysPost::user4p is valid)
// AstAlwaysPost::user4() -> AstIf*. Last IF (__Vdlyvset__) created under this AlwaysPost // AstAlwaysPost::user4() -> AstIf*. Last IF (__Vdlyvset__) created under this AlwaysPost
// Cleared each scope: // Cleared each scope/active:
// AstAssignDly::user3() -> AstVarScope*. __Vdlyvset__ created for this assign // AstAssignDly::user3() -> AstVarScope*. __Vdlyvset__ created for this assign
// AstAlwaysPost::user3() -> AstVarScope*. __Vdlyvset__ last referenced in IF // AstAlwaysPost::user3() -> AstVarScope*. __Vdlyvset__ last referenced in IF
AstUser1InUse m_inuser1; AstUser1InUse m_inuser1;
@ -144,6 +145,40 @@ private:
return varscp; return varscp;
} }
AstActive* createActivePost(AstVarRef* varrefp) {
AstActive* newactp = new AstActive (varrefp->fileline(), "sequentdly",
m_activep->sensesp());
m_activep->addNext(newactp);
return newactp;
}
void checkActivePost(AstVarRef* varrefp, AstActive* oldactivep) {
// Check for MULTIDRIVEN, and if so make new sentree that joins old & new sentree
if (!oldactivep) varrefp->v3fatalSrc("<= old dly assignment not put under sensitivity block");
if (oldactivep->sensesp() != m_activep->sensesp()) {
if (!varrefp->varp()->fileline()->warnIsOff(V3ErrorCode::MULTIDRIVEN)
&& !varrefp->varp()->user2()) {
varrefp->varp()->v3warn(MULTIDRIVEN,"Signal has multiple driving blocks: "<<varrefp->varp()->prettyName());
varrefp->v3warn(MULTIDRIVEN,"... Location of first driving block");
oldactivep->v3warn(MULTIDRIVEN,"... Location of other driving block");
varrefp->varp()->user2(true);
}
UINFO(4,"AssignDupDlyVar: "<<varrefp<<endl);
UINFO(4," Act: "<<m_activep<<endl);
UINFO(4," Act: "<<oldactivep<<endl);
// Make a new sensitivity list, which is the combination of both blocks
AstNodeSenItem* sena = m_activep->sensesp()->sensesp()->cloneTree(true);
AstNodeSenItem* senb = oldactivep->sensesp()->sensesp()->cloneTree(true);
AstSenTree* treep = new AstSenTree(m_activep->fileline(), sena);
if (senb) treep->addSensesp(senb);
if (AstSenTree* storep = oldactivep->sensesStorep()) {
storep->unlinkFrBack();
pushDeletep(storep);
}
oldactivep->sensesStorep(treep);
oldactivep->sensesp(treep);
}
}
AstNode* createDlyArray(AstAssignDly* nodep, AstNode* lhsp) { AstNode* createDlyArray(AstAssignDly* nodep, AstNode* lhsp) {
// Create delayed assignment // Create delayed assignment
// See top of this file for transformation // See top of this file for transformation
@ -225,6 +260,7 @@ private:
// //
//=== Setting/not setting boolean: __Vdlyvset__ //=== Setting/not setting boolean: __Vdlyvset__
AstVarScope* setvscp; AstVarScope* setvscp;
AstAssignPre* setinitp = NULL;
if (nodep->user3p()) { if (nodep->user3p()) {
// Simplistic optimization. If the previous statement in same scope was also a =>, // Simplistic optimization. If the previous statement in same scope was also a =>,
@ -236,11 +272,9 @@ private:
} else { // Create new one } else { // Create new one
string setvarname = (string("__Vdlyvset__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum)); string setvarname = (string("__Vdlyvset__")+oldvarp->shortName()+"__v"+cvtToStr(modVecNum));
setvscp = createVarSc(varrefp->varScopep(), setvarname, 1); setvscp = createVarSc(varrefp->varScopep(), setvarname, 1);
AstAssignPre* setinitp setinitp = new AstAssignPre (nodep->fileline(),
= new AstAssignPre (nodep->fileline(), new AstVarRef(nodep->fileline(), setvscp, true),
new AstVarRef(nodep->fileline(), setvscp, true), new AstConst(nodep->fileline(), 0));
new AstConst(nodep->fileline(), 0));
m_activep->addStmtsp(setinitp);
AstAssign* setassignp AstAssign* setassignp
= new AstAssign (nodep->fileline(), = new AstAssign (nodep->fileline(),
new AstVarRef(nodep->fileline(), setvscp, true), new AstVarRef(nodep->fileline(), setvscp, true),
@ -269,11 +303,18 @@ private:
UINFO(9," For "<<setvscp<<endl); UINFO(9," For "<<setvscp<<endl);
UINFO(9," & "<<varrefp<<endl); UINFO(9," & "<<varrefp<<endl);
AstAlwaysPost* finalp = varrefp->varScopep()->user4p()->castNode()->castAlwaysPost(); AstAlwaysPost* finalp = varrefp->varScopep()->user4p()->castNode()->castAlwaysPost();
if (!finalp) { if (finalp) {
AstActive* oldactivep = finalp->user2p()->castNode()->castActive();
checkActivePost(varrefp, oldactivep);
if (setinitp) oldactivep->addStmtsp(setinitp);
} else { // first time we've dealt with this memory
finalp = new AstAlwaysPost(nodep->fileline(), NULL/*sens*/, NULL/*body*/); finalp = new AstAlwaysPost(nodep->fileline(), NULL/*sens*/, NULL/*body*/);
UINFO(9," Created "<<finalp<<endl); UINFO(9," Created "<<finalp<<endl);
m_activep->addStmtsp(finalp); AstActive* newactp = createActivePost(varrefp);
newactp->addStmtsp(finalp);
varrefp->varScopep()->user4p(finalp); varrefp->varScopep()->user4p(finalp);
finalp->user2p(newactp);
if (setinitp) newactp->addStmtsp(setinitp);
} }
AstIf* postLogicp; AstIf* postLogicp;
if (finalp->user3p()->castNode() == setvscp) { if (finalp->user3p()->castNode() == setvscp) {
@ -292,7 +333,6 @@ private:
finalp->user4p(postLogicp); // and the associated IF, as we may be able to reuse it finalp->user4p(postLogicp); // and the associated IF, as we may be able to reuse it
} }
postLogicp->addIfsp(new AstAssign(nodep->fileline(), selectsp, valreadp)); postLogicp->addIfsp(new AstAssign(nodep->fileline(), selectsp, valreadp));
return newlhsp; return newlhsp;
} }
@ -316,6 +356,7 @@ private:
m_activep = nodep; m_activep = nodep;
bool oldinit = m_inInitial; bool oldinit = m_inInitial;
m_inInitial = nodep->hasInitial(); m_inInitial = nodep->hasInitial();
AstNode::user3ClearTree(); // Two sets to same variable in different actives must use different vars.
nodep->iterateChildren(*this); nodep->iterateChildren(*this);
m_inInitial = oldinit; m_inInitial = oldinit;
} }
@ -342,6 +383,7 @@ private:
m_inDly = false; m_inDly = false;
m_nextDlyp = NULL; m_nextDlyp = NULL;
} }
virtual void visit(AstVarRef* nodep, AstNUser*) { virtual void visit(AstVarRef* nodep, AstNUser*) {
if (!nodep->user2Inc()) { // Not done yet if (!nodep->user2Inc()) { // Not done yet
if (m_inDly && nodep->lvalue()) { if (m_inDly && nodep->lvalue()) {
@ -354,30 +396,7 @@ private:
AstVarScope* dlyvscp = oldvscp->user1p()->castNode()->castVarScope(); AstVarScope* dlyvscp = oldvscp->user1p()->castNode()->castVarScope();
if (dlyvscp) { // Multiple use of delayed variable if (dlyvscp) { // Multiple use of delayed variable
AstActive* oldactivep = dlyvscp->user2p()->castNode()->castActive(); AstActive* oldactivep = dlyvscp->user2p()->castNode()->castActive();
if (!oldactivep) nodep->v3fatalSrc("<= old dly assignment not put under sensitivity block"); checkActivePost(nodep, oldactivep);
if (oldactivep->sensesp() != m_activep->sensesp()) {
if (!nodep->varp()->fileline()->warnIsOff(V3ErrorCode::MULTIDRIVEN)
&& !nodep->varp()->user2()) {
nodep->varp()->v3warn(MULTIDRIVEN,"Signal has multiple driving blocks: "<<nodep->varp()->prettyName());
nodep->v3warn(MULTIDRIVEN,"... Location of first driving block");
oldactivep->v3warn(MULTIDRIVEN,"... Location of other driving block");
nodep->varp()->user2(true);
}
UINFO(4,"AssignDupDlyVar: "<<nodep<<endl);
UINFO(4," Act: "<<m_activep<<endl);
UINFO(4," Act: "<<oldactivep<<endl);
// Make a new sensitivity list, which is the combination of both blocks
AstNodeSenItem* sena = m_activep->sensesp()->sensesp()->cloneTree(true);
AstNodeSenItem* senb = oldactivep->sensesp()->sensesp()->cloneTree(true);
AstSenTree* treep = new AstSenTree(m_activep->fileline(), sena);
if (senb) treep->addSensesp(senb);
if (AstSenTree* storep = oldactivep->sensesStorep()) {
storep->unlinkFrBack();
pushDeletep(storep);
}
oldactivep->sensesStorep(treep);
oldactivep->sensesp(treep);
}
} }
if (!dlyvscp) { // First use of this delayed variable if (!dlyvscp) { // First use of this delayed variable
string newvarname = (string("__Vdly__")+nodep->varp()->shortName()); string newvarname = (string("__Vdly__")+nodep->varp()->shortName());
@ -393,12 +412,10 @@ private:
postp->lhsp()->user2(true); // Don't detect this assignment postp->lhsp()->user2(true); // Don't detect this assignment
oldvscp->user1p(dlyvscp); // So we can find it later oldvscp->user1p(dlyvscp); // So we can find it later
// Make new ACTIVE with identical sensitivity tree // Make new ACTIVE with identical sensitivity tree
AstActive* newactp = new AstActive (nodep->fileline(), "sequentdly", AstActive* newactp = createActivePost(nodep);
m_activep->sensesp()); dlyvscp->user2p(newactp);
newactp->addStmtsp(prep); // Add to FRONT of statements newactp->addStmtsp(prep); // Add to FRONT of statements
newactp->addStmtsp(postp); newactp->addStmtsp(postp);
m_activep->addNext(newactp);
dlyvscp->user2p(newactp);
} }
AstVarRef* newrefp = new AstVarRef(nodep->fileline(), dlyvscp, true); AstVarRef* newrefp = new AstVarRef(nodep->fileline(), dlyvscp, true);
newrefp->user2(true); // No reason to do it again newrefp->user2(true); // No reason to do it again

View File

@ -0,0 +1,30 @@
#!/usr/bin/perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2008 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 (
v_flags2 => ["--lint-only"],
fails=>1,
verilator_make_gcc => 0,
make_top_shell => 0,
make_main => 0,
expect=>
'%Warning-MULTIDRIVEN: t/t_lint_multidriven_bad.v:\d+: Signal has multiple driving blocks: v.mem
%Warning-MULTIDRIVEN: Use ".*" and lint_on around source to disable this message.
%Warning-MULTIDRIVEN: t/t_lint_multidriven_bad.v:\d+: ... Location of first driving block
%Warning-MULTIDRIVEN: t/t_lint_multidriven_bad.v:\d+: ... Location of other driving block
%Warning-MULTIDRIVEN: t/t_lint_multidriven_bad.v:\d+: Signal has multiple driving blocks: out2
%Warning-MULTIDRIVEN: t/t_lint_multidriven_bad.v:\d+: ... Location of first driving block
%Warning-MULTIDRIVEN: t/t_lint_multidriven_bad.v:\d+: ... Location of other driving block
%Error: Exiting due to.*',
);
ok(1);
1;

View File

@ -0,0 +1,37 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2012 by Wilson Snyder.
module t (/*AUTOARG*/
// Outputs
out, out2,
// Inputs
clk, a0, d0, d1
);
input clk;
input [1:0] a0;
input [7:0] d0;
input [7:0] d1;
output reg [31:0] out;
output reg [15:0] out2;
reg [7:0] mem [4];
always @(posedge clk) begin
mem[a0] <= d0;
end
always @(negedge clk) begin
mem[a0] <= d1;
end
assign out = {mem[3],mem[2],mem[1],mem[0]};
always @(posedge clk) begin
out2[7:0] <= d0;
end
always @(negedge clk) begin
out2[15:8] <= d0;
end
endmodule

18
test_regress/t/t_mem_twoedge.pl Executable file
View File

@ -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;

View File

@ -0,0 +1,117 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed into the Public Domain, for any use,
// without warranty, 2012 by Wilson Snyder.
module t (/*AUTOARG*/
// Inputs
clk
);
input clk;
integer cyc=0;
reg [63:0] crc;
reg [63:0] sum;
// verilator lint_off MULTIDRIVEN
/*AUTOWIRE*/
// Beginning of automatic wires (for undeclared instantiated-module outputs)
wire [31:0] out; // From test of Test.v
wire [15:0] out2; // From test of Test.v
// End of automatics
// verilator lint_on MULTIDRIVEN
Test test (
.en (crc[21:20]),
.a1 (crc[19:18]),
.a0 (crc[17:16]),
.d1 (crc[15:8]),
.d0 (crc[7:0]),
/*AUTOINST*/
// Outputs
.out (out[31:0]),
.out2 (out2[15:0]),
// Inputs
.clk (clk));
// Aggregate outputs into a single result vector
wire [63:0] result = {out2, 16'h0, out};
// Test loop
`ifdef TEST_VERBOSE
always @ (negedge clk) begin
$write("[%0t] cyc==%0d crc=%x result=%x\n",$time, cyc, crc, result);
end
`endif
always @ (posedge clk) begin
`ifdef TEST_VERBOSE
$write("[%0t] cyc==%0d crc=%x result=%x\n",$time, cyc, crc, result);
`endif
cyc <= cyc + 1;
crc <= {crc[62:0], crc[63]^crc[2]^crc[0]};
sum <= result ^ {sum[62:0],sum[63]^sum[2]^sum[0]};
if (cyc==0) begin
// Setup
crc <= 64'h5aef0c8d_d70a4497;
sum <= 64'h0;
test.clear();
end
else if (cyc<10) begin
sum <= 64'h0;
test.clear();
end
else if (cyc<90) begin
end
else if (cyc==99) begin
$write("[%0t] cyc==%0d crc=%x sum=%x\n",$time, cyc, crc, sum);
if (crc !== 64'hc77bb9b3784ea091) $stop;
// What checksum will we end up with (above print should match)
`define EXPECTED_SUM 64'hc68a94a34ec970aa
if (sum !== `EXPECTED_SUM) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule
module Test (/*AUTOARG*/
// Outputs
out, out2,
// Inputs
clk, en, a0, a1, d0, d1
);
input clk;
input [1:0] en;
input [1:0] a0;
input [1:0] a1;
input [7:0] d0;
input [7:0] d1;
output reg [31:0] out;
output reg [15:0] out2;
// verilator lint_off MULTIDRIVEN
reg [7:0] mem [4];
// verilator lint_on MULTIDRIVEN
task clear();
for (int i=0; i<4; ++i) mem[i] = 0;
endtask
always @(posedge clk) begin
if (en[0]) begin
mem[a0] <= d0;
out2[7:0] <= d0;
end
end
always @(negedge clk) begin
if (en[1]) begin
mem[a1] <= d1;
out2[15:8] <= d0;
end
end
assign out = {mem[3],mem[2],mem[1],mem[0]};
endmodule