diff --git a/Changes b/Changes index 8bb6ae714..21622bb2c 100644 --- a/Changes +++ b/Changes @@ -7,6 +7,8 @@ indicates the contributor was also the author of the fix; Thanks! **** Support $bits(data_type), bug327. [Alex Solomatnikov] +**** Fix concatenates and vectored bufif1, bug326. [Iztok Jeras] + * Verilator 3.811 2011/02/14 **** Report errors on duplicated or empty pins, bug321. [Christian Leber] diff --git a/src/V3AstNodes.h b/src/V3AstNodes.h index 0bf61910f..85d18f13f 100644 --- a/src/V3AstNodes.h +++ b/src/V3AstNodes.h @@ -3152,6 +3152,19 @@ struct AstReplicate : public AstNodeBiop { virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} virtual int instrCount() const { return widthInstrs()*2; } }; +struct AstBufIf1 : public AstNodeBiop { + // lhs is enable, rhs is data to drive + AstBufIf1(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) { + if (lhsp) widthSignedFrom(lhsp); } + ASTNODE_NODE_FUNCS(BufIf1, BUFIF1) + virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opBufIf1(lhs,rhs); } + virtual string emitVerilog() { return "bufif(%r,%l)"; } + virtual string emitC() { V3ERROR_NA; return false;} // Lclean || Rclean + virtual string emitSimpleOperator() { V3ERROR_NA; return false;} // Lclean || Rclean + virtual bool cleanOut() {V3ERROR_NA; return false;} // Lclean || Rclean + virtual bool cleanLhs() {return false;} virtual bool cleanRhs() {return false;} + virtual bool sizeMattersLhs() {return false;} virtual bool sizeMattersRhs() {return false;} +}; struct AstFGetS : public AstNodeBiop { AstFGetS(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {} ASTNODE_NODE_FUNCS(FGetS, FGETS) diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 80dd2fb23..14a8fe0b7 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -1061,6 +1061,24 @@ private: nodep->replaceWith(newp); nodep->deleteTree(); nodep=NULL; } + void replaceSelIntoBiop(AstSel* nodep) { + // SEL(BUFIF1(a,b),1,bit) => BUFIF1(SEL(a,1,bit),SEL(b,1,bit)) + AstNodeBiop* fromp = nodep->fromp()->unlinkFrBack()->castNodeBiop(); + if (!fromp) nodep->v3fatalSrc("Called on non biop"); + AstNode* lsbp = nodep->lsbp()->unlinkFrBack(); + AstNode* widthp = nodep->widthp()->unlinkFrBack(); + // + AstNode* bilhsp = fromp->lhsp()->unlinkFrBack(); + AstNode* birhsp = fromp->rhsp()->unlinkFrBack(); + // + fromp->lhsp(new AstSel(nodep->fileline(), + bilhsp, lsbp->cloneTree(true), widthp->cloneTree(true))); + fromp->rhsp(new AstSel(nodep->fileline(), + birhsp, lsbp, widthp)); + fromp->widthSignedFrom(nodep); + nodep->replaceWith(fromp); nodep->deleteTree(); nodep=NULL; + } + virtual void visit(AstVarRef* nodep, AstNUser*) { nodep->iterateChildren(*this); if (!nodep->varp()) nodep->v3fatalSrc("Not linked"); @@ -1767,6 +1785,10 @@ private: TREEOPC("AstSel{$fromp.castConst, $lsbp.castConst, $widthp.castConst, }", "replaceConst(nodep)"); TREEOPV("AstSel{$fromp.castConcat, $lsbp.castConst, $widthp.castConst, }", "replaceSelConcat(nodep)"); TREEOPV("AstSel{$fromp.castReplicate, $lsbp.castConst, $widthp.isOne, }", "replaceSelReplicate(nodep)"); + // V3Tristate requires selects below BufIf1. + // We can probably extend this to additional logical operators, but only definite + // win if bit select is a constant (otherwise we may need to compute bit index several times) + TREEOPV("AstSel{$fromp.castBufIf1}", "replaceSelIntoBiop(nodep)"); // Conversions TREEOPV("AstRedXnor{$lhsp}", "AstNot{AstRedXor{$lhsp}}"); // Just eliminate XNOR's TREEOPV("AstLogIf {$lhsp, $rhsp}", "AstLogOr{AstLogNot{$lhsp},$rhsp}"); diff --git a/src/V3Number.cpp b/src/V3Number.cpp index 82231a86a..23247548c 100644 --- a/src/V3Number.cpp +++ b/src/V3Number.cpp @@ -1318,6 +1318,15 @@ V3Number& V3Number::opPowS (const V3Number& lhs, const V3Number& rhs) { return setAllBitsX(); } +V3Number& V3Number::opBufIf1 (const V3Number& ens, const V3Number& if1s) { + setZero(); + for(int bit=0; bitwidth(); bit++) { + if (ens.bitIs1(bit)) { setBit(bit, if1s.bitIs(bit)); } + else setBit(bit,'z'); + } + return *this; +} + V3Number& V3Number::opAssign (const V3Number& lhs) { // Note may be a width change during the assign setZero(); diff --git a/src/V3Number.h b/src/V3Number.h index d1c8bae5c..5e3235218 100644 --- a/src/V3Number.h +++ b/src/V3Number.h @@ -195,6 +195,7 @@ public: V3Number& opCaseNeq (const V3Number& lhs, const V3Number& rhs); V3Number& opWildEq (const V3Number& lhs, const V3Number& rhs); V3Number& opWildNeq (const V3Number& lhs, const V3Number& rhs); + V3Number& opBufIf1 (const V3Number& lhs, const V3Number& rhs); // "standard" math V3Number& opNot (const V3Number& lhs); V3Number& opLogNot (const V3Number& lhs); diff --git a/src/V3Signed.cpp b/src/V3Signed.cpp index 3b7160f0b..49f224ffb 100644 --- a/src/V3Signed.cpp +++ b/src/V3Signed.cpp @@ -98,6 +98,7 @@ private: virtual void visit(AstLogOr* nodep, AstNUser*) { signed_Ou_Ix(nodep); } virtual void visit(AstLogIf* nodep, AstNUser*) { signed_Ou_Ix(nodep); } virtual void visit(AstLogIff* nodep, AstNUser*) { signed_Ou_Ix(nodep); } + virtual void visit(AstBufIf1* nodep, AstNUser*) { signed_Ou_Ix(nodep); } // ... These shouldn't matter, just make unsigned virtual void visit(AstScopeName* nodep, AstNUser*) { signed_Ou_Ix(nodep); } virtual void visit(AstText* nodep, AstNUser*) { signed_Ou_Ix(nodep); } diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index f3089b8f9..3d4c02717 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -159,8 +159,8 @@ private: // assign x = (OE) ? 'hz : y; // see if this a COND and separate out the __en logic from the output logic if it is - if (nodep->rhsp()->castCond()) { - AstCond* condp = nodep->rhsp()->castCond(); + if (AstCond* condp = nodep->rhsp()->castCond()) { + //if (debug()>=9) nodep->dumpTree(cout,"- cond-in: "); AstNode* oep = condp->condp(); AstNode* expr1p = condp->expr1p(); AstNode* expr2p = condp->expr2p(); @@ -192,17 +192,44 @@ private: // replace the old assign logic with the new one AstAssignW* newassp = new AstAssignW(nodep->fileline(), outp,outrhsp); + //if (debug()>=9) newassp->dumpTreeAndNext(cout,"- cond-out: "); nodep->replaceWith(newassp); nodep->deleteTree(); nodep=NULL; newassp->iterateChildren(*this); - } else { + } + // How about a tri gate? + else if (AstBufIf1* bufp = nodep->rhsp()->castBufIf1()) { + //if (debug()>=9) nodep->dumpTree(cout,"- tri-in : "); + AstNode* enrhsp = bufp->lhsp()->unlinkFrBack(); + AstNode* outrhsp = bufp->rhsp()->unlinkFrBack(); + + AstNode* outp = nodep->lhsp()->unlinkFrBack();; + AstVarRef* outrefp = NULL; + if (outp->castVarRef()) { + outrefp = outp->castVarRef(); + } else if (outp->castSel()) { + outrefp = outp->castSel()->fromp()->castVarRef(); + } else { + nodep->v3error("Can't find LHS varref"); + } + + createEnableVar(outp, outrefp, enrhsp, outrhsp->width()); + + // replace the old assign logic with the new one + AstAssignW* newassp = new AstAssignW(nodep->fileline(), outp,outrhsp); + //if (debug()>=9) newassp->dumpTreeAndNext(cout,"- tri-out: "); + nodep->replaceWith(newassp); + nodep->deleteTree(); nodep=NULL; + newassp->iterateChildren(*this); + } + else { nodep->iterateChildren(*this); } } AstVar* createEnableVar(AstNode* outp, AstVarRef* outrefp, AstNode* enrhsp, int width, string suffix="") { - // this function creates an __en Var that cooresponds to + // this function creates an __en Var that corresponds to // the outp and outrefp and creates an assignw to enrhsp AstVar* enp = new AstVar (outrefp->varp()->fileline(), AstVarType::MODULETEMP, @@ -218,10 +245,14 @@ private: enrhsp->v3error("Don't know how to deal with selection logic wider than 1 bit"); } } + + AstNode* newassp = new AstAssignW (enp->fileline(), + new AstVarRef (enp->fileline(), enp, true), + enrhsp); + if (debug()>=9) enp->dumpTreeAndNext(cout,"- cev-out: "); + if (debug()>=9) newassp->dumpTreeAndNext(cout,"- cev-out: "); m_modp->addStmtp(enp); - m_modp->addStmtp(new AstAssignW (enp->fileline(), - new AstVarRef (enp->fileline(), enp, true), - enrhsp)); + m_modp->addStmtp(newassp); outrefp->user1p(enp); // put __en signal into varref for later usage outrefp->varp()->user1p(enp); // put __en signal into var as well in the event this is a single lhs driver and this needs passed up one level diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 23856cb91..3e7b30398 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -143,6 +143,7 @@ private: virtual void visit(AstOr* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } virtual void visit(AstXnor* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } virtual void visit(AstXor* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } + virtual void visit(AstBufIf1* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } // Multiple possible reasonable division width conversions. Just keep our code simple, they aren't common. virtual void visit(AstModDiv* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } virtual void visit(AstModDivS* nodep, AstNUser* vup) { width_Omax_L_Rlhs(nodep,vup); } diff --git a/src/verilog.y b/src/verilog.y index 7f91cea22..fb9a3be56 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -2682,46 +2682,58 @@ gateUnsupList: ; gateBuf: - gateIdE instRangeE '(' idClassSel ',' expr ')' { $$ = new AstAssignW ($3,$4,$6); } + gateIdE instRangeE '(' variable_lvalue ',' expr ')' + { $$ = new AstAssignW ($3,$4,$6); } ; gateBufif0: - gateIdE instRangeE '(' idClassSel ',' expr ',' expr ')' { $$ = new AstAssignW ($3,$4,new AstCond($3,$8, new AstConst($3,V3Number($3,"1'bz")), $6)); } + gateIdE instRangeE '(' variable_lvalue ',' expr ',' expr ')' + { $$ = new AstAssignW ($3,$4,new AstBufIf1($3,new AstNot($3,$8),$6)); } ; gateBufif1: - gateIdE instRangeE '(' idClassSel ',' expr ',' expr ')' { $$ = new AstAssignW ($3,$4,new AstCond($3,$8, $6, new AstConst($3,V3Number($3,"1'bz")))); } + gateIdE instRangeE '(' variable_lvalue ',' expr ',' expr ')' + { $$ = new AstAssignW ($3,$4,new AstBufIf1($3,$8,$6)); } ; gateNot: - gateIdE instRangeE '(' idClassSel ',' expr ')' { $$ = new AstAssignW ($3,$4,new AstNot($5,$6)); } + gateIdE instRangeE '(' variable_lvalue ',' expr ')' + { $$ = new AstAssignW ($3,$4,new AstNot($5,$6)); } ; gateNotif0: - gateIdE instRangeE '(' idClassSel ',' expr ',' expr ')' { $$ = new AstAssignW ($3,$4,new AstCond($3,$8, new AstConst($3,V3Number($3,"1'bz")), new AstNot($3, $6))); } + gateIdE instRangeE '(' variable_lvalue ',' expr ',' expr ')' + { $$ = new AstAssignW ($3,$4,new AstBufIf1($3,new AstNot($3,$8), new AstNot($3, $6))); } ; gateNotif1: - gateIdE instRangeE '(' idClassSel ',' expr ',' expr ')' { $$ = new AstAssignW ($3,$4,new AstCond($3,$8, new AstNot($3,$6), new AstConst($3,V3Number($3,"1'bz")))); } + gateIdE instRangeE '(' variable_lvalue ',' expr ',' expr ')' + { $$ = new AstAssignW ($3,$4,new AstBufIf1($3,$8, new AstNot($3,$6))); } ; gateAnd: - gateIdE instRangeE '(' idClassSel ',' gateAndPinList ')' { $$ = new AstAssignW ($3,$4,$6); } + gateIdE instRangeE '(' variable_lvalue ',' gateAndPinList ')' + { $$ = new AstAssignW ($3,$4,$6); } ; gateNand: - gateIdE instRangeE '(' idClassSel ',' gateAndPinList ')' { $$ = new AstAssignW ($3,$4,new AstNot($5,$6)); } + gateIdE instRangeE '(' variable_lvalue ',' gateAndPinList ')' + { $$ = new AstAssignW ($3,$4,new AstNot($5,$6)); } ; gateOr: - gateIdE instRangeE '(' idClassSel ',' gateOrPinList ')' { $$ = new AstAssignW ($3,$4,$6); } + gateIdE instRangeE '(' variable_lvalue ',' gateOrPinList ')' + { $$ = new AstAssignW ($3,$4,$6); } ; gateNor: - gateIdE instRangeE '(' idClassSel ',' gateOrPinList ')' { $$ = new AstAssignW ($3,$4,new AstNot($5,$6)); } + gateIdE instRangeE '(' variable_lvalue ',' gateOrPinList ')' + { $$ = new AstAssignW ($3,$4,new AstNot($5,$6)); } ; gateXor: - gateIdE instRangeE '(' idClassSel ',' gateXorPinList ')' { $$ = new AstAssignW ($3,$4,$6); } + gateIdE instRangeE '(' variable_lvalue ',' gateXorPinList ')' + { $$ = new AstAssignW ($3,$4,$6); } ; gateXnor: - gateIdE instRangeE '(' idClassSel ',' gateXorPinList ')' { $$ = new AstAssignW ($3,$4,new AstNot($5,$6)); } + gateIdE instRangeE '(' variable_lvalue ',' gateXorPinList ')' + { $$ = new AstAssignW ($3,$4,new AstNot($5,$6)); } ; gatePullup: - gateIdE instRangeE '(' idClassSel ')' { $$ = new AstPull ($3, $4, true); } + gateIdE instRangeE '(' variable_lvalue ')' { $$ = new AstPull ($3, $4, true); } ; gatePulldown: - gateIdE instRangeE '(' idClassSel ')' { $$ = new AstPull ($3, $4, false); } + gateIdE instRangeE '(' variable_lvalue ')' { $$ = new AstPull ($3, $4, false); } ; gateUnsup: gateIdE instRangeE '(' gateUnsupPinList ')' { $$ = new AstImplicit ($3,$4); } diff --git a/test_regress/t/t_tri_array_bufif.pl b/test_regress/t/t_tri_array_bufif.pl new file mode 100755 index 000000000..7058e622f --- /dev/null +++ b/test_regress/t/t_tri_array_bufif.pl @@ -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; diff --git a/test_regress/t/t_tri_array_bufif.v b/test_regress/t/t_tri_array_bufif.v new file mode 100644 index 000000000..e767faced --- /dev/null +++ b/test_regress/t/t_tri_array_bufif.v @@ -0,0 +1,116 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2011 by Wilson Snyder. + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + integer cyc=0; + reg [63:0] crc; + reg [63:0] sum; + + parameter DW = 4; + wire [3:0] drv_a = crc[3:0]; + wire [3:0] drv_b = crc[7:4]; + wire [3:0] drv_e = crc[19:16]; + + /*AUTOWIRE*/ + // Beginning of automatic wires (for undeclared instantiated-module outputs) + wire [DW-1:0] drv; // To/From test1 of Test1.v + wire [DW-1:0] drv2; // From test2 of Test2.v + // End of automatics + + Test1 test1 (/*AUTOINST*/ + // Inouts + .drv (drv[DW-1:0]), + // Inputs + .drv_a (drv_a[DW-1:0]), + .drv_b (drv_b[DW-1:0]), + .drv_e (drv_e[DW-1:0])); + Test2 test2 (/*AUTOINST*/ + // Outputs + .drv2 (drv2[DW-1:0]), + // Inputs + .drv_a (drv_a[DW-1:0]), + .drv_b (drv_b[DW-1:0]), + .drv_e (drv_e[DW-1:0])); + + // Aggregate outputs into a single result vector + wire [63:0] result = {60'h0, drv}; + + // Test loop + always @ (posedge clk) begin +`ifdef TEST_VERBOSE + $write("[%0t] cyc==%0d crc=%x drv=%x %x (%b??%b:%b)\n",$time, cyc, crc, drv, drv2, drv_e,drv_a,drv_b); +`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; + end + else if (cyc<10) begin + sum <= 64'h0; + end + else if (cyc<90) begin + if (drv2 != drv) $stop; + 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'hd95d216c5a2945d0 + if (sum !== `EXPECTED_SUM) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + end + +endmodule + +module Test1 #( + parameter DW = 4 +)( + input wire [DW-1:0] drv_a, + input wire [DW-1:0] drv_b, + input wire [DW-1:0] drv_e, + inout wire [DW-1:0] drv +); + + wire drv_0, drv_1, drv_2, drv_3; + bufif1 bufa0 (drv_0, drv_a[0], drv_e[0]); + bufif1 bufb0 (drv_0, drv_b[0], ~drv_e[0]); + bufif1 bufa1 (drv_1, drv_a[1], drv_e[1]); + bufif1 bufb1 (drv_1, drv_b[1], ~drv_e[1]); + bufif1 bufa2 (drv_2, drv_a[2], drv_e[2]); + bufif1 bufb2 (drv_2, drv_b[2], ~drv_e[2]); + bufif1 bufa3 (drv_3, drv_a[3], drv_e[3]); + bufif1 bufb3 (drv_3, drv_b[3], ~drv_e[3]); + assign drv = {drv_3,drv_2,drv_1,drv_0}; + +endmodule + +module Test2 #( + parameter DW = 4 +)( + input wire [DW-1:0] drv_a, + input wire [DW-1:0] drv_b, + input wire [DW-1:0] drv_e, + inout wire [DW-1:0] drv2 +); + + wire [DW-1:0] drv_all; + bufif1 bufa [DW-1:0] (drv_all, drv_a, drv_e); + // Below ~= bufif1 bufb [DW-1:0] (drv_all, drv_b, ~drv_e); + bufif1 bufb [DW-1:0] ({drv_all[3], drv_all[2], drv_all[1], drv_all[0]}, + {drv_b[3], drv_b[2], drv_b[1], drv_b[0]}, + {~drv_e[3], ~drv_e[2], ~drv_e[1], ~drv_e[0]}); + assign drv2 = drv_all; + +endmodule