Support case inside, bug708.
This commit is contained in:
parent
3a23afb0bc
commit
2d61e0270e
1
Changes
1
Changes
|
|
@ -5,6 +5,7 @@ indicates the contributor was also the author of the fix; Thanks!
|
|||
|
||||
* Verilator 3.856 devel
|
||||
|
||||
*** Support case inside, bug708. [Jan Egil Ruud]
|
||||
|
||||
|
||||
* Verilator 3.855 2014-01-18
|
||||
|
|
|
|||
17
src/V3Ast.h
17
src/V3Ast.h
|
|
@ -496,22 +496,23 @@ public:
|
|||
|
||||
//######################################################################
|
||||
|
||||
class AstCaseType {
|
||||
class VCaseType {
|
||||
public:
|
||||
enum en {
|
||||
CT_CASE,
|
||||
CT_CASEX,
|
||||
CT_CASEZ
|
||||
CT_CASEZ,
|
||||
CT_CASEINSIDE
|
||||
};
|
||||
enum en m_e;
|
||||
inline AstCaseType () : m_e(CT_CASE) {}
|
||||
inline AstCaseType (en _e) : m_e(_e) {}
|
||||
explicit inline AstCaseType (int _e) : m_e(static_cast<en>(_e)) {}
|
||||
inline VCaseType () : m_e(CT_CASE) {}
|
||||
inline VCaseType (en _e) : m_e(_e) {}
|
||||
explicit inline VCaseType (int _e) : m_e(static_cast<en>(_e)) {}
|
||||
operator en () const { return m_e; }
|
||||
};
|
||||
inline bool operator== (AstCaseType lhs, AstCaseType rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (AstCaseType lhs, AstCaseType::en rhs) { return (lhs.m_e == rhs); }
|
||||
inline bool operator== (AstCaseType::en lhs, AstCaseType rhs) { return (lhs == rhs.m_e); }
|
||||
inline bool operator== (VCaseType lhs, VCaseType rhs) { return (lhs.m_e == rhs.m_e); }
|
||||
inline bool operator== (VCaseType lhs, VCaseType::en rhs) { return (lhs.m_e == rhs); }
|
||||
inline bool operator== (VCaseType::en lhs, VCaseType rhs) { return (lhs == rhs.m_e); }
|
||||
|
||||
//######################################################################
|
||||
|
||||
|
|
|
|||
|
|
@ -112,6 +112,35 @@ int AstNodeClassDType::widthAlignBytes() const {
|
|||
else return 8;
|
||||
}
|
||||
|
||||
AstNodeBiop* AstEq::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
||||
if (lhsp->isDouble() && rhsp->isDouble()) {
|
||||
return new AstEqD(fl, lhsp, rhsp);
|
||||
} else {
|
||||
return new AstEq(fl, lhsp, rhsp);
|
||||
}
|
||||
}
|
||||
|
||||
AstNodeBiop* AstGte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
||||
if (lhsp->isDouble() && rhsp->isDouble()) {
|
||||
return new AstGteD(fl, lhsp, rhsp);
|
||||
} else if (lhsp->isSigned() && rhsp->isSigned()) {
|
||||
return new AstGteS(fl, lhsp, rhsp);
|
||||
} else {
|
||||
return new AstGte(fl, lhsp, rhsp);
|
||||
}
|
||||
}
|
||||
|
||||
AstNodeBiop* AstLte::newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp) {
|
||||
if (lhsp->isDouble() && rhsp->isDouble()) {
|
||||
return new AstLteD(fl, lhsp, rhsp);
|
||||
} else if (lhsp->isSigned() && rhsp->isSigned()) {
|
||||
return new AstLteS(fl, lhsp, rhsp);
|
||||
} else {
|
||||
return new AstLte(fl, lhsp, rhsp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool AstVar::isSigPublic() const {
|
||||
return (m_sigPublic || (v3Global.opt.allPublic() && !isTemp() && !isGenVar()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2053,14 +2053,14 @@ struct AstCase : public AstNodeCase {
|
|||
// exprp Children: MATHs
|
||||
// casesp Children: CASEITEMs
|
||||
private:
|
||||
AstCaseType m_casex; // 0=case, 1=casex, 2=casez
|
||||
VCaseType m_casex; // 0=case, 1=casex, 2=casez
|
||||
bool m_fullPragma; // Synthesis full_case
|
||||
bool m_parallelPragma; // Synthesis parallel_case
|
||||
bool m_uniquePragma; // unique case
|
||||
bool m_unique0Pragma; // unique0 case
|
||||
bool m_priorityPragma; // priority case
|
||||
public:
|
||||
AstCase(FileLine* fileline, AstCaseType casex, AstNode* exprp, AstNode* casesp)
|
||||
AstCase(FileLine* fileline, VCaseType casex, AstNode* exprp, AstNode* casesp)
|
||||
: AstNodeCase(fileline, exprp, casesp) {
|
||||
m_casex=casex;
|
||||
m_fullPragma=false; m_parallelPragma=false;
|
||||
|
|
@ -2070,8 +2070,11 @@ public:
|
|||
virtual string verilogKwd() const { return casez()?"casez":casex()?"casex":"case"; }
|
||||
virtual bool same(AstNode* samep) const {
|
||||
return m_casex==samep->castCase()->m_casex; }
|
||||
bool casex() const { return m_casex==AstCaseType::CT_CASEX; }
|
||||
bool casez() const { return m_casex==AstCaseType::CT_CASEZ; }
|
||||
bool casex() const { return m_casex==VCaseType::CT_CASEX; }
|
||||
bool casez() const { return m_casex==VCaseType::CT_CASEZ; }
|
||||
bool caseInside() const { return m_casex==VCaseType::CT_CASEINSIDE; }
|
||||
bool caseSimple() const { return m_casex==VCaseType::CT_CASE; }
|
||||
void caseInsideSet() { m_casex=VCaseType::CT_CASEINSIDE; }
|
||||
bool fullPragma() const { return m_fullPragma; }
|
||||
void fullPragma(bool flag) { m_fullPragma=flag; }
|
||||
bool parallelPragma() const { return m_parallelPragma; }
|
||||
|
|
@ -3610,6 +3613,7 @@ struct AstEq : public AstNodeBiCom {
|
|||
AstEq(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiCom(fl, lhsp, rhsp) {
|
||||
dtypeSetLogicBool(); }
|
||||
ASTNODE_NODE_FUNCS(Eq, EQ)
|
||||
static AstNodeBiop* newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp); // Return AstEq/AstEqD
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opEq(lhs,rhs); }
|
||||
virtual string emitVerilog() { return "%k(%l %f== %r)"; }
|
||||
virtual string emitC() { return "VL_EQ_%lq(%lW, %P, %li, %ri)"; }
|
||||
|
|
@ -3740,6 +3744,7 @@ struct AstGte : public AstNodeBiop {
|
|||
AstGte(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {
|
||||
dtypeSetLogicBool(); }
|
||||
ASTNODE_NODE_FUNCS(Gte, GTE)
|
||||
static AstNodeBiop* newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp); // Return AstGte/AstGteS/AstGteD
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opGte(lhs,rhs); }
|
||||
virtual string emitVerilog() { return "%k(%l %f>= %r)"; }
|
||||
virtual string emitC() { return "VL_GTE_%lq(%lW, %P, %li, %ri)"; }
|
||||
|
|
@ -3779,6 +3784,7 @@ struct AstLte : public AstNodeBiop {
|
|||
AstLte(FileLine* fl, AstNode* lhsp, AstNode* rhsp) : AstNodeBiop(fl, lhsp, rhsp) {
|
||||
dtypeSetLogicBool(); }
|
||||
ASTNODE_NODE_FUNCS(Lte, LTE)
|
||||
static AstNodeBiop* newTyped(FileLine* fl, AstNode* lhsp, AstNode* rhsp); // Return AstLte/AstLteS/AstLteD
|
||||
virtual void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) { out.opLte(lhs,rhs); }
|
||||
virtual string emitVerilog() { return "%k(%l %f<= %r)"; }
|
||||
virtual string emitC() { return "VL_LTE_%lq(%lW, %P, %li, %ri)"; }
|
||||
|
|
|
|||
|
|
@ -97,7 +97,8 @@ private:
|
|||
nodep->v3error("Use of x/? constant in generate case statement, (no such thing as 'generate casez')");
|
||||
} else if (m_caseExprp->castCase() && m_caseExprp->castCase()->casex()) {
|
||||
// Don't sweat it, we already complained about casex in general
|
||||
} else if (m_caseExprp->castCase() && m_caseExprp->castCase()->casez()) {
|
||||
} else if (m_caseExprp->castCase() && (m_caseExprp->castCase()->casez()
|
||||
|| m_caseExprp->castCase()->caseInside())) {
|
||||
if (nodep->num().isUnknown()) {
|
||||
nodep->v3warn(CASEWITHX, "Use of x constant in casez statement, (perhaps intended ?/z in constant)");
|
||||
}
|
||||
|
|
@ -322,36 +323,42 @@ private:
|
|||
icondNextp = icondp->nextp();
|
||||
icondp->unlinkFrBack();
|
||||
|
||||
AstNode* and1p;
|
||||
AstNode* and2p;
|
||||
AstNode* condp = NULL; // Default is to use and1p/and2p
|
||||
AstConst* iconstp = icondp->castConst();
|
||||
if (iconstp && neverItem(nodep, iconstp)) {
|
||||
// X in casez can't ever be executed
|
||||
icondp->deleteTree(); icondp=NULL; iconstp=NULL;
|
||||
// For simplicity, make expression that is not equal, and let later
|
||||
// optimizations remove it
|
||||
and1p = new AstConst(itemp->fileline(), AstConst::LogicFalse());
|
||||
and2p = new AstConst(itemp->fileline(), AstConst::LogicTrue());
|
||||
condp = new AstConst(itemp->fileline(), AstConst::LogicFalse());
|
||||
} else if (AstInsideRange* irangep = icondp->castInsideRange()) {
|
||||
// Similar logic in V3Width::visit(AstInside)
|
||||
AstNode* ap = AstGte::newTyped(itemp->fileline(),
|
||||
cexprp->cloneTree(false),
|
||||
irangep->lhsp()->unlinkFrBack());
|
||||
AstNode* bp = AstLte::newTyped(itemp->fileline(),
|
||||
cexprp->cloneTree(false),
|
||||
irangep->rhsp()->unlinkFrBack());
|
||||
condp = new AstAnd(itemp->fileline(), ap, bp);
|
||||
} else if (iconstp && iconstp->num().isFourState()
|
||||
&& (nodep->casex() || nodep->casez())) {
|
||||
&& (nodep->casex() || nodep->casez() || nodep->caseInside())) {
|
||||
V3Number nummask (itemp->fileline(), iconstp->width());
|
||||
nummask.opBitsNonX(iconstp->num());
|
||||
V3Number numval (itemp->fileline(), iconstp->width());
|
||||
numval.opBitsOne(iconstp->num());
|
||||
and1p = new AstAnd(itemp->fileline(), cexprp->cloneTree(false),
|
||||
new AstConst(itemp->fileline(), nummask));
|
||||
and2p = new AstAnd(itemp->fileline(),
|
||||
new AstConst(itemp->fileline(), numval),
|
||||
new AstConst(itemp->fileline(), nummask));
|
||||
AstNode* and1p = new AstAnd(itemp->fileline(), cexprp->cloneTree(false),
|
||||
new AstConst(itemp->fileline(), nummask));
|
||||
AstNode* and2p = new AstAnd(itemp->fileline(),
|
||||
new AstConst(itemp->fileline(), numval),
|
||||
new AstConst(itemp->fileline(), nummask));
|
||||
icondp->deleteTree(); icondp=NULL; iconstp=NULL;
|
||||
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
|
||||
} else {
|
||||
// Not a caseX mask, we can simply build CASEEQ(cexpr icond)
|
||||
and1p = cexprp->cloneTree(false);
|
||||
and2p = icondp;
|
||||
AstNode* and1p = cexprp->cloneTree(false);
|
||||
AstNode* and2p = icondp;
|
||||
condp = AstEq::newTyped(itemp->fileline(), and1p, and2p);
|
||||
}
|
||||
AstNodeBiop* condp = (and1p->isDouble()
|
||||
? (new AstEqD(itemp->fileline(), and1p, and2p))->castNodeBiop()
|
||||
: (new AstEq(itemp->fileline(), and1p, and2p))->castNodeBiop());
|
||||
if (!ifexprp) {
|
||||
ifexprp = condp;
|
||||
} else {
|
||||
|
|
@ -435,7 +442,7 @@ private:
|
|||
bool neverItem(AstCase* casep, AstConst* itemp) {
|
||||
// Xs in case or casez are impossible due to two state simulations
|
||||
if (casep->casex()) {
|
||||
} else if (casep->casez()) {
|
||||
} else if (casep->casez() || casep->caseInside()) {
|
||||
if (itemp->num().isUnknown()) return true;
|
||||
} else {
|
||||
if (itemp->num().isFourState()) return true;
|
||||
|
|
|
|||
|
|
@ -504,7 +504,7 @@ private:
|
|||
UINFO(6," -to "<<pinNewVarp<<endl);
|
||||
pinNewVarp->user2p(connectRefp);
|
||||
// Public output inside the cell must go via an assign rather than alias
|
||||
// Else the public logic will set the alias, loosing the value to be propagated up
|
||||
// Else the public logic will set the alias, losing the value to be propagated up
|
||||
// (InOnly isn't a problem as the AssignAlias will create the assignment for us)
|
||||
pinNewVarp->user3(pinNewVarp->isSigUserRWPublic() && pinNewVarp->isOutOnly());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,13 +163,13 @@ private:
|
|||
m_assignwp = NULL;
|
||||
}
|
||||
virtual void visit(AstCaseItem* nodep, AstNUser*) {
|
||||
m_constXCvt = false; // Avoid loosing the X's in casex
|
||||
m_constXCvt = false; // Avoid losing the X's in casex
|
||||
nodep->condsp()->iterateAndNext(*this);
|
||||
m_constXCvt = true;
|
||||
nodep->bodysp()->iterateAndNext(*this);
|
||||
}
|
||||
virtual void visit(AstNodeDType* nodep, AstNUser*) {
|
||||
m_constXCvt = false; // Avoid loosing the X's in casex
|
||||
m_constXCvt = false; // Avoid losing the X's in casex
|
||||
nodep->iterateChildren(*this);
|
||||
m_constXCvt = true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1050,6 +1050,7 @@ private:
|
|||
nextip = itemp->nextp(); // Will be unlinking
|
||||
AstNode* inewp;
|
||||
if (AstInsideRange* irangep = itemp->castInsideRange()) {
|
||||
// Similar logic in V3Case
|
||||
inewp = new AstAnd(itemp->fileline(),
|
||||
new AstGte(itemp->fileline(),
|
||||
nodep->exprp()->cloneTree(true),
|
||||
|
|
|
|||
|
|
@ -2190,7 +2190,12 @@ statement_item<nodep>: // IEEE: statement_item
|
|||
if ($1 == uniq_UNIQUE0) $2->unique0Pragma(true);
|
||||
if ($1 == uniq_PRIORITY) $2->priorityPragma(true); }
|
||||
//UNSUP caseStart caseAttrE yMATCHES case_patternListE yENDCASE { }
|
||||
//UNSUP caseStart caseAttrE yINSIDE case_insideListE yENDCASE { }
|
||||
| unique_priorityE caseStart caseAttrE yINSIDE case_insideListE yENDCASE { $$ = $2; if ($5) $2->addItemsp($5);
|
||||
if (!$2->caseSimple()) $2->v3error("Illegal to have inside on a casex/casez");
|
||||
$2->caseInsideSet();
|
||||
if ($1 == uniq_UNIQUE) $2->uniquePragma(true);
|
||||
if ($1 == uniq_UNIQUE0) $2->unique0Pragma(true);
|
||||
if ($1 == uniq_PRIORITY) $2->priorityPragma(true); }
|
||||
//
|
||||
// // IEEE: conditional_statement
|
||||
| unique_priorityE yIF '(' expr ')' stmtBlock %prec prLOWER_THAN_ELSE
|
||||
|
|
@ -2328,9 +2333,9 @@ unique_priorityE<uniqstate>: // IEEE: unique_priority + empty
|
|||
;
|
||||
|
||||
caseStart<casep>: // IEEE: part of case_statement
|
||||
yCASE '(' expr ')' { $$ = GRAMMARP->m_caseAttrp = new AstCase($1,AstCaseType::CT_CASE,$3,NULL); }
|
||||
| yCASEX '(' expr ')' { $$ = GRAMMARP->m_caseAttrp = new AstCase($1,AstCaseType::CT_CASEX,$3,NULL); }
|
||||
| yCASEZ '(' expr ')' { $$ = GRAMMARP->m_caseAttrp = new AstCase($1,AstCaseType::CT_CASEZ,$3,NULL); }
|
||||
yCASE '(' expr ')' { $$ = GRAMMARP->m_caseAttrp = new AstCase($1,VCaseType::CT_CASE,$3,NULL); }
|
||||
| yCASEX '(' expr ')' { $$ = GRAMMARP->m_caseAttrp = new AstCase($1,VCaseType::CT_CASEX,$3,NULL); }
|
||||
| yCASEZ '(' expr ')' { $$ = GRAMMARP->m_caseAttrp = new AstCase($1,VCaseType::CT_CASEZ,$3,NULL); }
|
||||
;
|
||||
|
||||
caseAttrE:
|
||||
|
|
@ -2344,6 +2349,11 @@ case_itemListE<caseitemp>: // IEEE: [ { case_item } ]
|
|||
| case_itemList { $$ = $1; }
|
||||
;
|
||||
|
||||
case_insideListE<caseitemp>: // IEEE: [ { case_inside_item } ]
|
||||
/* empty */ { $$ = NULL; }
|
||||
| case_inside_itemList { $$ = $1; }
|
||||
;
|
||||
|
||||
case_itemList<caseitemp>: // IEEE: { case_item + ... }
|
||||
caseCondList ':' stmtBlock { $$ = new AstCaseItem($2,$1,$3); }
|
||||
| yDEFAULT ':' stmtBlock { $$ = new AstCaseItem($2,NULL,$3); }
|
||||
|
|
@ -2353,6 +2363,15 @@ case_itemList<caseitemp>: // IEEE: { case_item + ... }
|
|||
| case_itemList yDEFAULT ':' stmtBlock { $$ = $1;$1->addNext(new AstCaseItem($3,NULL,$4)); }
|
||||
;
|
||||
|
||||
case_inside_itemList<caseitemp>: // IEEE: { case_inside_item + open_range_list ... }
|
||||
open_range_list ':' stmtBlock { $$ = new AstCaseItem($2,$1,$3); }
|
||||
| yDEFAULT ':' stmtBlock { $$ = new AstCaseItem($2,NULL,$3); }
|
||||
| yDEFAULT stmtBlock { $$ = new AstCaseItem($1,NULL,$2); }
|
||||
| case_inside_itemList open_range_list ':' stmtBlock { $$ = $1;$1->addNext(new AstCaseItem($3,$2,$4)); }
|
||||
| case_inside_itemList yDEFAULT stmtBlock { $$ = $1;$1->addNext(new AstCaseItem($2,NULL,$3)); }
|
||||
| case_inside_itemList yDEFAULT ':' stmtBlock { $$ = $1;$1->addNext(new AstCaseItem($3,NULL,$4)); }
|
||||
;
|
||||
|
||||
open_range_list<nodep>: // ==IEEE: open_range_list + open_value_range
|
||||
open_value_range { $$ = $1; }
|
||||
| open_range_list ',' open_value_range { $$ = $1;$1->addNext($3); }
|
||||
|
|
|
|||
|
|
@ -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,66 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed into the Public Domain, for any use,
|
||||
// without warranty, 2014 by Wilson Snyder.
|
||||
|
||||
module t (/*AUTOARG*/
|
||||
// Inputs
|
||||
clk
|
||||
);
|
||||
|
||||
input clk;
|
||||
|
||||
integer cyc; initial cyc=0;
|
||||
reg [63:0] crc;
|
||||
reg [63:0] sum;
|
||||
|
||||
reg out1;
|
||||
reg [4:0] out2;
|
||||
sub sub (.in(crc[23:0]), .out1(out1), .out2(out2));
|
||||
|
||||
always @ (posedge clk) begin
|
||||
`ifdef TEST_VERBOSE
|
||||
$write("[%0t] cyc==%0d crc=%x sum=%x in[3:0]=%x out=%x,%x\n",$time, cyc, crc, sum, crc[3:0], out1,out2);
|
||||
`endif
|
||||
cyc <= cyc + 1;
|
||||
crc <= {crc[62:0], crc[63]^crc[2]^crc[0]};
|
||||
sum <= {sum[62:0], sum[63]^sum[2]^sum[0]} ^ {58'h0,out1,out2};
|
||||
if (cyc==0) begin
|
||||
// Setup
|
||||
crc <= 64'h00000000_00000097;
|
||||
sum <= 64'h0;
|
||||
end
|
||||
else if (cyc==99) begin
|
||||
$write("[%0t] cyc==%0d crc=%x sum=%x\n",$time, cyc, crc, sum);
|
||||
`define EXPECTED_SUM 64'h10204fa5567c8a4b
|
||||
if (sum !== `EXPECTED_SUM) $stop;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
||||
module sub (/*AUTOARG*/
|
||||
// Outputs
|
||||
out1, out2,
|
||||
// Inputs
|
||||
in
|
||||
);
|
||||
|
||||
input [23:0] in;
|
||||
output reg out1;
|
||||
output reg [4:0] out2;
|
||||
|
||||
always @* begin
|
||||
case (in[3:0]) inside
|
||||
default: {out1,out2} = {1'b0,5'h0F}; // Note not last item
|
||||
4'h1, 4'h2, 4'h3: {out1,out2} = {1'b1,5'h01};
|
||||
4'h4: {out1,out2} = {1'b1,5'h04};
|
||||
[4'h6:4'h5]: {out1,out2} = {1'b1,5'h05}; // order backwards, will not match
|
||||
4'b100?:/*8,9*/ {out1,out2} = {1'b1,5'h08};
|
||||
[4'hc:4'hf]: {out1,out2} = {1'b1,5'h0C};
|
||||
endcase
|
||||
end
|
||||
|
||||
endmodule
|
||||
Loading…
Reference in New Issue