diff --git a/Changes b/Changes index 6cee4da28..6b3b8d739 100644 --- a/Changes +++ b/Changes @@ -5,6 +5,8 @@ indicates the contributor was also the author of the fix; Thanks! * Verilator 3.656 2008/*** +**** Fix assignments of {a,b,c} = {c,b,a}. [Jonathan Kimmitt] + **** Fix Perl warning with --lint-only. [by Ding Xiaoliang] **** Avoid creating obj_dir with --lint-only. [Ding Xiaoliang] diff --git a/TODO b/TODO index a76c935ff..39cf96e27 100644 --- a/TODO +++ b/TODO @@ -52,6 +52,10 @@ Usability: Better reporting of unopt problems, including what lines of code Report more errors (all of them?) before exiting [Eugene Weber] +Internal Code: + Eliminate the AstNUser* passed to all visitors; its only needed in V3Width, + and removing it will speed up and simplify all the other code. + Performance: Constant propagation Extra cleaning AND: 1 & ((VARREF >> 1) | ((&VARREF >> 1) & VARREF)) diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 54d5ebff7..c266ef826 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -39,11 +39,63 @@ #include "V3Width.h" #include "V3Signed.h" +//###################################################################### +// Utilities + +class ConstVarMarkVisitor : public AstNVisitor { + // NODE STATE + // AstVar::userp -> bool, Var marked, 0=not set yet +private: + // VISITORS + virtual void visit(AstVarRef* nodep, AstNUser*) { + if (nodep->varp()) nodep->varp()->user(1); + } + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } +public: + // CONSTUCTORS + ConstVarMarkVisitor(AstNode* nodep) { + AstNode::userClearTree(); + nodep->iterateAndNext(*this, NULL); + } + virtual ~ConstVarMarkVisitor() {} +}; + +class ConstVarFindVisitor : public AstNVisitor { + // NODE STATE + // AstVar::userp -> bool, input from ConstVarMarkVisitor + // MEMBERS + bool m_found; +private: + // VISITORS + virtual void visit(AstVarRef* nodep, AstNUser*) { + if (nodep->varp() && nodep->varp()->user()) m_found = true; + } + virtual void visit(AstNode* nodep, AstNUser*) { + nodep->iterateChildren(*this); + } +public: + // CONSTUCTORS + ConstVarFindVisitor(AstNode* nodep) { + m_found = false; + nodep->iterateAndNext(*this, NULL); + } + virtual ~ConstVarFindVisitor() {} + // METHODS + bool found() const { return m_found; } +}; + //###################################################################### // Const state, as a visitor of each AstNode class ConstVisitor : public AstNVisitor { private: + // NODE STATE + // ** only when m_warn is set. If state is needed other times, + // ** must track down everywhere V3Const is called and make sure no overlaps. + // AstVar::userp -> Used by ConstVarMarkVisitor/ConstVarFindVisitor + // STATE bool m_params; // If true, propogate parameterized and true numbers only bool m_required; // If true, must become a constant @@ -494,10 +546,26 @@ private: } } else if (!m_cpp && nodep->lhsp()->castConcat()) { - UINFO(4," ASSI "<castAssignDly()) { // Is same var on LHS and RHS? + ConstVarMarkVisitor mark(nodep->lhsp()); + ConstVarFindVisitor find(nodep->rhsp()); + if (find.found()) need_temp = true; + } + if (need_temp) { + // The first time we constify, there may be the same variable on the LHS + // and RHS. In that case, we must use temporaries, or {a,b}={b,a} will break. + UINFO(4," ASSITEMP "< ASSIGN(temp1,SEL(rhs,{size})), + // ASSIGN(temp2,SEL(newrhs,{size})) + // ASSIGN(lc1,temp1), + // ASSIGN(lc2,temp2) + } else { + UINFO(4," ASSI "< ASSIGN(lc1,SEL(rhs,{size})), + // ASSIGN(lc2,SEL(newrhs,{size})) + } if (debug()>=9) nodep->dumpTree(cout," Ass_old: "); - // ASSIGN(CONCAT(lc1,lc2),rhs) -> ASSIGN(lc1,SEL(rhs,{size})), - // ASSIGN(lc2,SEL(newrhs,{size})) // Unlink the stuff AstNode* lc1p = nodep->lhsp()->castConcat()->lhsp()->unlinkFrBack(); AstNode* lc2p = nodep->lhsp()->castConcat()->rhsp()->unlinkFrBack(); @@ -517,14 +585,50 @@ private: sel2p->width(msb2-lsb2+1,msb2-lsb2+1); // Make new assigns of same flavor as old one //*** Not cloneTree; just one node. - AstNodeAssign* asn1p=nodep->cloneType(lc1p, sel1p)->castNodeAssign(); - AstNodeAssign* asn2p=nodep->cloneType(lc2p, sel2p)->castNodeAssign(); - asn1p->width(msb1-lsb1+1,msb1-lsb1+1); - asn2p->width(msb2-lsb2+1,msb2-lsb2+1); - nodep->addNextHere(asn1p); - nodep->addNextHere(asn2p); - if (debug()>=9) asn1p->dumpTree(cout," _new: "); - if (debug()>=9) asn2p->dumpTree(cout," _new: "); + AstNode* newp = NULL; + if (!need_temp) { + AstNodeAssign* asn1ap=nodep->cloneType(lc1p, sel1p)->castNodeAssign(); + AstNodeAssign* asn2ap=nodep->cloneType(lc2p, sel2p)->castNodeAssign(); + asn1ap->width(msb1-lsb1+1,msb1-lsb1+1); + asn2ap->width(msb2-lsb2+1,msb2-lsb2+1); + newp = newp->addNext(asn1ap); + newp = newp->addNext(asn2ap); + } else { + if (!m_modp) nodep->v3fatalSrc("Not under module"); + // We could create just one temp variable, but we'll get better optimization + // if we make one per term. + string name1 = ((string)"__Vconcswap__"+cvtToStr(m_modp->varNumGetInc())); + string name2 = ((string)"__Vconcswap__"+cvtToStr(m_modp->varNumGetInc())); + AstVar* temp1p = new AstVar(sel1p->fileline(), AstVarType::BLOCKTEMP, name1, + new AstRange(sel1p->fileline(), msb1-lsb1, 0)); + AstVar* temp2p = new AstVar(sel2p->fileline(), AstVarType::BLOCKTEMP, name2, + new AstRange(sel2p->fileline(), msb2-lsb2, 0)); + m_modp->addStmtp(temp1p); + m_modp->addStmtp(temp2p); + AstNodeAssign* asn1ap=nodep->cloneType + (new AstVarRef(sel1p->fileline(), temp1p, true), + sel1p)->castNodeAssign(); + AstNodeAssign* asn2ap=nodep->cloneType + (new AstVarRef(sel2p->fileline(), temp2p, true), + sel2p)->castNodeAssign(); + AstNodeAssign* asn1bp=nodep->cloneType + (lc1p, new AstVarRef(sel1p->fileline(), temp1p, false)) + ->castNodeAssign(); + AstNodeAssign* asn2bp=nodep->cloneType + (lc2p, new AstVarRef(sel2p->fileline(), temp2p, false)) + ->castNodeAssign(); + asn1ap->width(msb1-lsb1+1,msb1-lsb1+1); + asn1bp->width(msb1-lsb1+1,msb1-lsb1+1); + asn2ap->width(msb2-lsb2+1,msb2-lsb2+1); + asn2bp->width(msb2-lsb2+1,msb2-lsb2+1); + // This order matters + newp = newp->addNext(asn1ap); + newp = newp->addNext(asn2ap); + newp = newp->addNext(asn1bp); + newp = newp->addNext(asn2bp); + } + if (debug()>=9) newp->dumpTreeAndNext(cout," _new: "); + nodep->addNextHere(newp); // Cleanup nodep->unlinkFrBack()->deleteTree(); nodep=NULL; conp->deleteTree(); conp=NULL; diff --git a/test_regress/t/t_EXAMPLE.v b/test_regress/t/t_EXAMPLE.v index 6fc167f5e..378ef653e 100644 --- a/test_regress/t/t_EXAMPLE.v +++ b/test_regress/t/t_EXAMPLE.v @@ -14,7 +14,7 @@ // please note it here, otherwise:** // // This file ONLY is placed into the Public Domain, for any use, -// without warranty, 2007 by Wilson Snyder. +// without warranty, 2008 by Wilson Snyder. module t (/*AUTOARG*/ // Inputs diff --git a/test_regress/t/t_math_swap.pl b/test_regress/t/t_math_swap.pl new file mode 100755 index 000000000..7bfdbe852 --- /dev/null +++ b/test_regress/t/t_math_swap.pl @@ -0,0 +1,18 @@ +#!/usr/bin/perl +if (!$::Driver) { use FindBin; exec("./driver.pl", @ARGV, $0); die; } +# $Id$ +# 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 +# General Public License or the Perl Artistic License. + +compile ( + ); + +execute ( + check_finished=>1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_math_swap.v b/test_regress/t/t_math_swap.v new file mode 100644 index 000000000..d45ca54fa --- /dev/null +++ b/test_regress/t/t_math_swap.v @@ -0,0 +1,165 @@ +// $Id$ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2008 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + integer cyc=0; + reg [63:0] crc; + reg [63:0] sum; + + // Take CRC data and apply to testblock inputs + wire [31:0] Operand1 = crc[31:0]; + wire [15:0] Operand2 = crc[47:32]; + wire Unsigned = crc[48]; + reg rst; + + parameter wl = 16; + + /*AUTOWIRE*/ + // Beginning of automatic wires (for undeclared instantiated-module outputs) + wire [wl-1:0] Quotient; // From test of Test.v + wire [wl-1:0] Remainder; // From test of Test.v + // End of automatics + + Test test (/*AUTOINST*/ + // Outputs + .Quotient (Quotient[wl-1:0]), + .Remainder (Remainder[wl-1:0]), + // Inputs + .Operand1 (Operand1[wl*2-1:0]), + .Operand2 (Operand2[wl-1:0]), + .clk (clk), + .rst (rst), + .Unsigned (Unsigned)); + + // Aggregate outputs into a single result vector + wire [63:0] result = {32'h0, Quotient, Remainder}; + + // What checksum will we end up with +`define EXPECTED_SUM 64'h98d41f89a8be5693 + + // Test loop + always @ (posedge clk) begin +`ifdef TEST_VERBOSE + $write("[%0t] cyc==%0d crc=%x result=%x it=%x\n",$time, cyc, crc, result, test.Iteration); +`endif + cyc <= cyc + 1; + if (cyc < 20 || test.Iteration==4'd15) begin + crc <= {crc[62:0], crc[63]^crc[2]^crc[0]}; + end + sum <= result ^ {sum[62:0],sum[63]^sum[2]^sum[0]}; + if (cyc==0) begin + // Setup + crc <= 64'h5aef0c8d_d70a4497; + rst <= 1'b1; + end + else if (cyc<20) begin + sum <= 64'h0; + rst <= 1'b0; + 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'h8dd70a44972ad809) $stop; + if (sum !== `EXPECTED_SUM) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +module Test(clk, rst, Operand1, Operand2, Unsigned, Quotient, Remainder); + + parameter wl = 16; + + input [wl*2-1:0] Operand1; + input [wl-1:0] Operand2; + input clk, rst, Unsigned; + output [wl-1:0] Quotient, Remainder; + + reg Cy, Overflow, Sign1, Sign2, Zero, Negative; + reg [wl-1:0] ah,al,Quotient, Remainder; + reg [3:0] Iteration; + reg [wl-1:0] sub_quot,op; + reg ah_ext; + + reg [1:0] a,b,c,d,e; + + always @(posedge clk) begin + if (!rst) begin + {a,b,c,d,e} = Operand1[9:0]; + {a,b,c,d,e} = {e,d,c,b,a}; + if (a != Operand1[1:0]) $stop; + if (b != Operand1[3:2]) $stop; + if (c != Operand1[5:4]) $stop; + if (d != Operand1[7:6]) $stop; + if (e != Operand1[9:8]) $stop; + end + end + + always @(posedge clk) begin + if (rst) begin + Iteration <= 0; + Quotient <= 0; + Remainder <= 0; + end + else begin + if (Iteration == 0) begin + {ah,al} = Operand1; + op = Operand2; + Cy = 0; + Overflow = 0; + Sign1 = (~Unsigned)&ah[wl-1]; + Sign2 = (~Unsigned)&(ah[wl-1]^op[wl-1]); + if (Sign1) {ah,al} = -{ah,al}; + end +`define BUG1 +`ifdef BUG1 + {ah_ext,ah,al} = {ah,al,Cy}; +`else + ah_ext = ah[15]; + ah[15:1] = ah[14:0]; + ah[0] = al[15]; + al[15:1] = al[14:0]; + al[0] = Cy; +`endif +`ifdef TEST_VERBOSE + $display("%x %x %x %x %x %x %x %x %x", + Iteration, ah, al, Quotient, Remainder, Overflow, ah_ext, sub_quot, Cy); +`endif + {Cy,sub_quot} = (~Unsigned)&op[wl-1]? {ah_ext,ah}+op : {ah_ext,ah} - {1'b1,op}; + if (Cy) + begin + {ah_ext,ah} = {1'b0,sub_quot}; + end + if (Iteration != 15 ) + begin + if (ah_ext) Overflow = 1; + end + else + begin + if (al[14] && ~Unsigned) Overflow = 1; + Quotient <= Sign2 ? -{al[14:0],Cy} : {al[14:0],Cy}; + Remainder <= Sign1 ? -ah : ah; + if (Overflow) + begin + Quotient <= Sign2 ? 16'h8001 : {Unsigned,{15{1'b1}}}; + Remainder <= Unsigned ? 16'hffff : 16'h8000; + Zero = 1; + Negative = 1; + end + end + Iteration <= Iteration + 1; // Count number of times this instruction is repeated + end + end + +endmodule