2006-08-26 11:35:28 +00:00
// $Id$
//*************************************************************************
// DESCRIPTION: Verilator: Emit C++ for tree
//
// Code available from: http://www.veripool.com/verilator
//
// AUTHORS: Wilson Snyder with Paul Wasson, Duane Gabli
//
//*************************************************************************
//
// Copyright 2003-2006 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.
//
// Verilator is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
//*************************************************************************
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <math.h>
#include <map>
#include <vector>
#include <algorithm>
#include "V3Global.h"
#include "V3EmitC.h"
#include "V3EmitCBase.h"
#define VL_VALUE_STRING_MAX_WIDTH 1024 // We use a static char array in VL_VALUE_STRING
//######################################################################
// Coverage ID tracking
class EmitCoverIds {
// TYPES
typedef map < AstCoverDecl * , int > CoverIdMap ;
// MEMBERS
CoverIdMap m_coverIds ; // Remapping of coverage IDs to per-module value
int m_coverIdsSize ; // Size of m_coverIds (to avoid O(n^2) insert)
// METHODS
public :
void clear () {
m_coverIds . clear ();
m_coverIdsSize = 0 ;
}
int size () const { return m_coverIdsSize ; }
int remap ( AstCoverDecl * declp ) {
// Make cover ID for this point, unique on this module
CoverIdMap :: iterator it = m_coverIds . find ( declp );
if ( it == m_coverIds . end ()) {
m_coverIds . insert ( make_pair ( declp , m_coverIdsSize ++ )); // Remapping of coverage IDs to per-module value
return m_coverIdsSize - 1 ;
} else {
return it -> second ;
}
}
EmitCoverIds () : m_coverIdsSize ( 0 ) {}
~ EmitCoverIds () {}
};
//######################################################################
// Emit statements and math operators
class EmitCStmts : public EmitCBaseVisitor {
private :
bool m_suppressSemi ;
AstVarRef * m_wideTempRefp ; // Variable that _WW macros should be setting
vector < AstVar *> m_ctorVarsVec ; // All variables in constructor order
public :
EmitCoverIds m_coverIds ; // Coverage ID remapping
public :
//int debug() { return 9; }
// METHODS
void displayEmit ( AstDisplay * nodep );
string displayFormat ( AstNode * widthNode , string in ,
char fmtLetter , bool padZero , bool reallyString );
void displayArg ( AstDisplay * dispp , AstNode ** elistp , string fmt , char fmtLetter );
void emitVarDecl ( AstVar * nodep , const string & prefixIfImp );
2006-08-29 00:58:48 +00:00
typedef enum { EVL_IO , EVL_SIG , EVL_TEMP , EVL_STATIC , EVL_ALL } EisWhich ;
2006-08-26 11:35:28 +00:00
void emitVarList ( AstNode * firstp , EisWhich which , const string & prefixIfImp );
void emitVarCtors ();
bool emitSimpleOk ( AstNodeMath * nodep );
void emitIQW ( AstNode * nodep ) {
puts ( nodep -> isWide () ? "W" : ( nodep -> isQuad () ? "Q" : "I" ));
}
void emitOpName ( AstNode * nodep , const string & name );
string cFuncArgs ( AstCFunc * nodep ) {
// Return argument list for given C function
string args = nodep -> argTypes ();
2006-10-11 15:41:42 +00:00
// Might be a user function with argument list.
for ( AstNode * stmtp = nodep -> argsp (); stmtp ; stmtp = stmtp -> nextp ()) {
if ( AstVar * portp = stmtp -> castVar ()) {
if ( portp -> isIO () && ! portp -> isFuncReturn ()) {
if ( args != "" ) args += ", " ;
if ( portp -> isWide ()) {
if ( portp -> isInOnly ()) args += "const " ;
args += portp -> cType ();
args += " (& " + portp -> name ();
args += ")[" + cvtToStr ( portp -> widthWords ()) + "]" ;
} else {
args += portp -> cType ();
if ( portp -> isOutput ()) args += "&" ;
args += " " + portp -> name ();
2006-08-26 11:35:28 +00:00
}
}
}
}
return args ;
}
// VISITORS
virtual void visit ( AstNodeAssign * nodep , AstNUser * ) {
bool paren = true ; bool decind = false ;
if ( AstSel * selp = nodep -> lhsp () -> castSel ()) {
if ( selp -> widthMin () == 1 ) {
putbs ( "VL_ASSIGNBIT_" );
emitIQW ( selp -> fromp ());
if ( nodep -> rhsp () -> isAllOnesV ()) {
putbs ( "O(" );
} else {
putbs ( "I(" );
}
puts ( cvtToStr ( nodep -> widthMin ()) + "," );
selp -> lsbp () -> iterateAndNext ( * this ); puts ( ", " );
selp -> fromp () -> iterateAndNext ( * this ); puts ( ", " );
} else {
putbs ( "VL_ASSIGNSEL_" );
emitIQW ( selp -> fromp ());
putbs ( "II" );
emitIQW ( nodep -> rhsp ());
puts ( "(" );
puts ( cvtToStr ( nodep -> widthMin ()) + "," );
selp -> lsbp () -> iterateAndNext ( * this ); puts ( ", " );
selp -> fromp () -> iterateAndNext ( * this ); puts ( ", " );
}
} else if ( nodep -> lhsp () -> castVarRef ()
&& nodep -> lhsp () -> castVarRef () -> varp () -> isSc ()) {
putbs ( "VL_ASSIGN_" ); // Set a systemC variable
if ( nodep -> lhsp () -> castVarRef () -> varp () -> isScQuad ()) puts ( "SQ" );
else puts ( "S" );
emitIQW ( nodep );
puts ( "(" );
puts ( cvtToStr ( nodep -> widthMin ()) + "," );
nodep -> lhsp () -> iterateAndNext ( * this ); puts ( ", " );
} else if ( nodep -> rhsp () -> castVarRef ()
&& nodep -> rhsp () -> castVarRef () -> varp () -> isSc ()) {
putbs ( "VL_ASSIGN_" ); // Get a systemC variable
emitIQW ( nodep );
if ( nodep -> rhsp () -> castVarRef () -> varp () -> isScQuad ()) puts ( "SQ(" );
else puts ( "S(" );
puts ( cvtToStr ( nodep -> widthMin ()) + "," );
nodep -> lhsp () -> iterateAndNext ( * this ); puts ( ", " );
} else if ( nodep -> isWide ()
&& nodep -> lhsp () -> castVarRef ()
&& ! nodep -> rhsp () -> castVarRef ()
&& ! nodep -> rhsp () -> castArraySel ()) {
// Wide functions assign into the array directly, don't need separate assign statement
m_wideTempRefp = nodep -> lhsp () -> castVarRef ();
paren = false ;
} else if ( nodep -> isWide ()) {
putbs ( "VL_ASSIGN_W(" );
puts ( cvtToStr ( nodep -> widthMin ()) + "," );
nodep -> lhsp () -> iterateAndNext ( * this ); puts ( ", " );
} else {
paren = false ;
nodep -> lhsp () -> iterateAndNext ( * this );
puts ( " " );
ofp () -> blockInc (); decind = true ;
if ( ! nodep -> rhsp () -> castConst ()) ofp () -> putBreak ();
puts ( "= " );
}
nodep -> rhsp () -> iterateAndNext ( * this );
if ( paren ) puts ( ")" );
if ( decind ) ofp () -> blockDec ();
if ( ! m_suppressSemi ) puts ( "; \n " );
}
virtual void visit ( AstCCall * nodep , AstNUser * ) {
puts ( nodep -> hiername ());
puts ( nodep -> funcp () -> name ());
puts ( "(" );
2006-08-30 21:07:55 +00:00
puts ( nodep -> argTypes ());
bool comma = ( nodep -> argTypes () != "" );
2006-08-26 11:35:28 +00:00
for ( AstNode * subnodep = nodep -> argsp (); subnodep ; subnodep = subnodep -> nextp ()) {
2006-08-30 21:07:55 +00:00
if ( comma ) puts ( ", " );
2006-08-26 11:35:28 +00:00
subnodep -> accept ( * this );
2006-08-30 21:07:55 +00:00
comma = true ;
2006-08-26 11:35:28 +00:00
}
if ( nodep -> backp () -> castNodeMath () || nodep -> backp () -> castCReturn ()) {
// We should have a separate CCall for math and statement usage, but...
puts ( ")" );
} else {
puts ( "); \n " );
}
}
virtual void visit ( AstNodeCase * nodep , AstNUser * ) {
// In V3Case...
nodep -> v3fatalSrc ( "Case statements should have been reduced out \n " );
}
virtual void visit ( AstComment * nodep , AstNUser * ) {
puts (( string ) "// " + nodep -> name () + " at " + nodep -> fileline () -> ascii () + " \n " );
nodep -> iterateChildren ( * this );
}
virtual void visit ( AstCoverDecl * nodep , AstNUser * ) {
2006-08-30 01:14:29 +00:00
puts ( "__vlCoverInsert(" ); // As Declared in emitCoverageDecl
2006-08-26 11:35:28 +00:00
puts ( "&__Vcoverage[" );
puts ( cvtToStr ( m_coverIds . remap ( nodep ))); puts ( "]" );
puts ( ", \" " ); puts ( nodep -> fileline () -> filebasename ()); puts ( " \" " );
puts ( ", " ); puts ( cvtToStr ( nodep -> fileline () -> lineno ()));
puts ( ", " ); puts ( cvtToStr ( nodep -> column ()));
puts ( ", \" " ); puts ( nodep -> hier ()); puts ( " \" " );
puts ( ", \" " ); puts ( nodep -> typeText ()); puts ( " \" " );
puts ( ", \" " ); puts ( nodep -> comment ()); puts ( " \" " );
puts ( "); \n " );
}
virtual void visit ( AstCoverInc * nodep , AstNUser * ) {
2006-09-12 00:55:27 +00:00
puts ( "++this->__Vcoverage[" );
2006-08-26 11:35:28 +00:00
puts ( cvtToStr ( m_coverIds . remap ( nodep -> declp ()))); puts ( "]; \n " );
}
virtual void visit ( AstCReturn * nodep , AstNUser * ) {
puts ( "return (" );
nodep -> lhsp () -> iterateAndNext ( * this );
puts ( "); \n " );
}
virtual void visit ( AstDisplay * nodep , AstNUser * ); // BELOW
virtual void visit ( AstFOpen * nodep , AstNUser * ) {
nodep -> filep () -> iterateAndNext ( * this );
puts ( " = VL_FOPEN_" );
emitIQW ( nodep -> filenamep ());
emitIQW ( nodep -> modep ());
if ( nodep -> modep () -> width () > 4 * 8 ) nodep -> modep () -> v3error ( "$fopen mode should be <= 4 characters" );
puts ( "(" );
if ( nodep -> filenamep () -> isWide ()) puts ( cvtToStr ( nodep -> filenamep () -> widthWords ()));
if ( nodep -> filenamep () -> widthWords () > VL_TO_STRING_MAX_WORDS ) {
nodep -> v3error ( "String of " << nodep -> filenamep () -> width () << " bits exceeds hardcoded limit VL_TO_STRING_MAX_WORDS in verilatedos.h \n " );
}
putbs ( ", " );
nodep -> filenamep () -> iterateAndNext ( * this );
putbs ( ", " );
nodep -> modep () -> iterateAndNext ( * this );
puts ( "); \n " );
}
virtual void visit ( AstFClose * nodep , AstNUser * ) {
puts ( "if (" );
nodep -> filep () -> iterateAndNext ( * this );
puts ( ") { fclose (VL_CVT_Q_FP(" );
nodep -> filep () -> iterateAndNext ( * this );
puts ( ")); " );
nodep -> filep () -> iterateAndNext ( * this ); // For saftey, so user doesn't later WRITE with it.
puts ( "=0; } \n " );
}
virtual void visit ( AstWhile * nodep , AstNUser * ) {
nodep -> precondsp () -> iterateAndNext ( * this );
puts ( "while (" );
nodep -> condp () -> iterateAndNext ( * this );
puts ( ") { \n " );
nodep -> bodysp () -> iterateAndNext ( * this );
nodep -> precondsp () -> iterateAndNext ( * this ); // Need to recompute before next loop
puts ( "} \n " );
}
virtual void visit ( AstNodeIf * nodep , AstNUser * ) {
puts ( "if (" );
if ( nodep -> branchPred () != AstBranchPred :: UNKNOWN ) {
puts ( nodep -> branchPred (). ascii ()); puts ( "(" );
}
nodep -> condp () -> iterateAndNext ( * this );
if ( nodep -> branchPred () != AstBranchPred :: UNKNOWN ) puts ( ")" );
puts ( ") { \n " );
nodep -> ifsp () -> iterateAndNext ( * this );
if ( nodep -> elsesp ()) {
puts ( "} else { \n " );
nodep -> elsesp () -> iterateAndNext ( * this );
}
puts ( "} \n " );
}
virtual void visit ( AstStop * nodep , AstNUser * ) {
puts ( "vl_stop( \" " );
puts ( nodep -> fileline () -> filename ());
puts ( " \" ," );
puts ( cvtToStr ( nodep -> fileline () -> lineno ()));
puts ( ", \"\" ); \n " );
}
virtual void visit ( AstFinish * nodep , AstNUser * ) {
puts ( "vl_finish( \" " );
puts ( nodep -> fileline () -> filename ());
puts ( " \" ," );
puts ( cvtToStr ( nodep -> fileline () -> lineno ()));
puts ( ", \"\" ); \n " );
}
virtual void visit ( AstText * nodep , AstNUser * ) {
ofp () -> putsNoTracking ( nodep -> text ());
}
virtual void visit ( AstCStmt * nodep , AstNUser * ) {
nodep -> bodysp () -> iterateAndNext ( * this );
}
virtual void visit ( AstCMath * nodep , AstNUser * ) {
nodep -> bodysp () -> iterateAndNext ( * this );
}
virtual void visit ( AstUCStmt * nodep , AstNUser * ) {
puts ( "// $c statement at " + nodep -> fileline () -> ascii () + " \n " );
nodep -> bodysp () -> iterateAndNext ( * this );
puts ( " \n " );
}
virtual void visit ( AstUCFunc * nodep , AstNUser * ) {
puts ( " \n " );
puts ( "// $c function at " + nodep -> fileline () -> ascii () + " \n " );
nodep -> bodysp () -> iterateAndNext ( * this );
puts ( " \n " );
}
// Operators
virtual void visit ( AstNodeTermop * nodep , AstNUser * ) {
emitOpName ( nodep , nodep -> emitOperator ());
puts ( ")" );
}
virtual void visit ( AstNodeUniop * nodep , AstNUser * ) {
if ( emitSimpleOk ( nodep )) {
putbs ( "(" ); puts ( nodep -> emitSimpleOperator ()); puts ( " " );
} else {
emitOpName ( nodep , nodep -> emitOperator ());
}
nodep -> lhsp () -> iterateAndNext ( * this ); puts ( ")" );
}
virtual void visit ( AstNodeBiop * nodep , AstNUser * ) {
if ( emitSimpleOk ( nodep )) {
putbs ( "(" ); nodep -> lhsp () -> iterateAndNext ( * this );
puts ( " " ); putbs ( nodep -> emitSimpleOperator ()); puts ( " " );
} else {
emitOpName ( nodep , nodep -> emitOperator ());
nodep -> lhsp () -> iterateAndNext ( * this ); puts ( ", " );
}
nodep -> rhsp () -> iterateAndNext ( * this ); puts ( ")" );
}
virtual void visit ( AstRedXor * nodep , AstNUser * vup ) {
if ( nodep -> lhsp () -> isWide ()) {
visit ( nodep -> castNodeUniop (), vup );
} else {
putbs ( "VL_REDXOR_" );
puts ( cvtToStr ( nodep -> lhsp () -> widthPow2 ()));
puts ( "(" );
nodep -> lhsp () -> iterateAndNext ( * this );
puts ( ")" );
}
}
virtual void visit ( AstMulS * nodep , AstNUser * vup ) {
if ( nodep -> widthWords () > VL_MULS_MAX_WORDS ) {
nodep -> v3error ( "Signed multiply of " << nodep -> width () << " bits exceeds hardcoded limit VL_MULS_MAX_WORDS in verilatedos.h \n " );
}
visit ( nodep -> castNodeBiop (), vup );
}
virtual void visit ( AstCast * nodep , AstNUser * ) {
// Extending a value of the same word width is just a NOP.
if ( nodep -> size () > VL_WORDSIZE ) {
puts ( "(QData)(" );
} else {
puts ( "(IData)(" );
}
nodep -> lhsp () -> iterateAndNext ( * this );
puts ( ")" );
}
virtual void visit ( AstNodeCond * nodep , AstNUser * ) {
// Widths match up already, so we'll just use C++'s operator w/o any temps.
if ( nodep -> expr1p () -> isWide ()) {
emitOpName ( nodep , nodep -> emitOperator ());
nodep -> condp () -> iterateAndNext ( * this ); puts ( ", " );
nodep -> expr1p () -> iterateAndNext ( * this ); puts ( ", " );
nodep -> expr2p () -> iterateAndNext ( * this ); puts ( ")" );
} else {
putbs ( "(" );
nodep -> condp () -> iterateAndNext ( * this ); putbs ( " ? " );
nodep -> expr1p () -> iterateAndNext ( * this ); putbs ( " : " );
nodep -> expr2p () -> iterateAndNext ( * this ); puts ( ")" );
}
}
virtual void visit ( AstSel * nodep , AstNUser * ) {
// Note ASSIGN checks for this on a LHS
if ( nodep -> widthp () -> isOne ()) {
emitOpName ( nodep , "VL_BITSEL" );
nodep -> fromp () -> iterateAndNext ( * this ); puts ( ", " );
nodep -> lsbp () -> iterateAndNext ( * this ); puts ( ")" );
} else {
emitOpName ( nodep , "VL_SEL" );
nodep -> fromp () -> iterateAndNext ( * this ); puts ( ", " );
nodep -> lsbp () -> iterateAndNext ( * this ); puts ( ", " );
nodep -> widthp () -> iterateAndNext ( * this ); puts ( ")" );
}
}
virtual void visit ( AstReplicate * nodep , AstNUser * ) {
if ( nodep -> lhsp () -> widthMin () == 1 && ! nodep -> isWide ()) {
if ((( int ) nodep -> rhsp () -> castConst () -> asInt ()
* nodep -> lhsp () -> widthMin ()) != nodep -> widthMin ())
nodep -> v3fatalSrc ( "Replicate non-constant or width miscomputed" );
puts ( "VL_REPLICATE_" );
emitIQW ( nodep );
puts ( "OI(" );
puts ( cvtToStr ( nodep -> widthMin ()));
if ( nodep -> lhsp ()) { puts ( "," + cvtToStr ( nodep -> lhsp () -> widthMin ())); }
if ( nodep -> rhsp ()) { puts ( "," + cvtToStr ( nodep -> rhsp () -> widthMin ())); }
puts ( "," );
} else {
emitOpName ( nodep , nodep -> emitOperator ());
}
nodep -> lhsp () -> iterateAndNext ( * this ); puts ( ", " );
nodep -> rhsp () -> iterateAndNext ( * this ); puts ( ")" );
}
virtual void visit ( AstArraySel * nodep , AstNUser * ) {
nodep -> fromp () -> iterateAndNext ( * this ); putbs ( "[" );
nodep -> bitp () -> iterateAndNext ( * this ); puts ( "]" );
}
virtual void visit ( AstWordSel * nodep , AstNUser * ) {
nodep -> fromp () -> iterateAndNext ( * this ); puts ( "[" ); // Not putbs, as usually it's a small constant next
nodep -> bitp () -> iterateAndNext ( * this ); puts ( "]" );
}
// Terminals
virtual void visit ( AstVarRef * nodep , AstNUser * ) {
puts ( nodep -> hiername ());
puts ( nodep -> varp () -> name ());
}
void emitConstant ( AstConst * nodep , AstVarRef * assigntop , const string & assignString ) {
// Put out constant set to the specified variable, or given variable in a string
if ( nodep -> num (). isFourState ()) {
nodep -> v3error ( "Unsupported: 4-state numbers in this context" );
} else if ( nodep -> isWide ()) {
putbs ( "VL_CONST_W_" );
puts ( cvtToStr ( VL_WORDS_I ( nodep -> num (). minWidth ())));
puts ( "X(" );
puts ( cvtToStr ( nodep -> widthMin ()));
puts ( "," );
if ( ! assigntop ) {
puts ( assignString );
} else if ( assigntop -> castVarRef ()) {
puts ( assigntop -> hiername ());
puts ( assigntop -> varp () -> name ());
} else {
assigntop -> iterateAndNext ( * this );
}
for ( int word = VL_WORDS_I ( nodep -> num (). minWidth ()) - 1 ; word > 0 ; word -- ) {
ofp () -> printf ( ",0x%08x" , nodep -> num (). dataWord ( word ));
}
ofp () -> printf ( ",0x%08x)" , nodep -> num (). dataWord ( 0 ));
} else if ( nodep -> isQuad ()) {
vluint64_t num = nodep -> asQuad ();
if ( num < 10 ) ofp () -> printf ( "VL_ULL(%lld)" , ( long long ) num );
else ofp () -> printf ( "VL_ULL(0x%llx)" , ( long long ) num );
} else {
uint32_t num = nodep -> asInt ();
if ( num < 10 ) puts ( cvtToStr ( num ));
else ofp () -> printf ( "0x%x" , num );
//Unneeded-Causes %lx format warnings:
// if (!nodep->num().isSigned() && (num & (1UL<<31))) puts("U");
}
}
void emitSetVarConstant ( const string & assignString , AstConst * constp ) {
if ( ! constp -> isWide ()) {
puts ( assignString );
puts ( " = " );
}
emitConstant ( constp , NULL , assignString );
puts ( "; \n " );
}
virtual void visit ( AstConst * nodep , AstNUser * ) {
if ( nodep -> isWide ()) {
if ( ! m_wideTempRefp ) nodep -> v3fatalSrc ( "Wide Constant w/ no temp" );
emitConstant ( nodep , m_wideTempRefp , "" );
m_wideTempRefp = NULL ; // We used it, barf if set it a second time
} else {
emitConstant ( nodep , NULL , "" );
}
}
// Just iterate
virtual void visit ( AstTopScope * nodep , AstNUser * ) {
nodep -> iterateChildren ( * this );
}
virtual void visit ( AstScope * nodep , AstNUser * ) {
nodep -> iterateChildren ( * this );
}
// NOPs
virtual void visit ( AstPragma * , AstNUser * ) {}
virtual void visit ( AstCell * , AstNUser * ) {} // Handled outside the Visit class
virtual void visit ( AstVar * , AstNUser * ) {} // Handled outside the Visit class
virtual void visit ( AstNodeText * , AstNUser * ) {} // Handled outside the Visit class
virtual void visit ( AstTraceDecl * , AstNUser * ) {} // Handled outside the Visit class
virtual void visit ( AstTraceInc * , AstNUser * ) {} // Handled outside the Visit class
// Default
virtual void visit ( AstNode * nodep , AstNUser * ) {
puts (( string ) " \n ???? // " + nodep -> typeName () + " \n " );
nodep -> iterateChildren ( * this );
nodep -> v3fatalSrc ( "Unknown node type reached emitter: " << nodep -> typeName ());
}
public :
EmitCStmts () {
m_suppressSemi = false ;
m_wideTempRefp = NULL ;
}
virtual ~ EmitCStmts () {}
};
//######################################################################
// Internal EmitC implementation
class EmitCImp : EmitCStmts {
// MEMBERS
AstModule * m_modp ;
vector < AstChangeDet *> m_blkChangeDetVec ; // All encountered changes in block
bool m_slow ; // Creating __Slow file
bool m_fast ; // Creating non __Slow file (or both)
int m_splitSize ; // # of cfunc nodes placed into output file
int m_splitFilenum ; // File number being created, 0 = primary
//---------------------------------------
// METHODS
void doubleOrDetect ( AstChangeDet * changep , bool & gotOne ) {
static int addDoubleOr = 10 ; // Determined experimentally as best
if ( ! changep -> rhsp ()) {
if ( ! gotOne ) gotOne = true ;
else puts ( " | " );
changep -> lhsp () -> iterateAndNext ( * this );
}
else {
AstVarRef * lhsp = changep -> lhsp () -> castVarRef ();
AstVarRef * rhsp = changep -> rhsp () -> castVarRef ();
if ( ! lhsp ) changep -> v3fatalSrc ( "Not ref?" );
if ( ! rhsp ) changep -> v3fatalSrc ( "Not ref?" );
for ( int word = 0 ; word < changep -> lhsp () -> widthWords (); word ++ ) {
if ( ! gotOne ) {
gotOne = true ;
addDoubleOr = 10 ; // Determined experimentally as best
puts ( "(" );
} else if ( -- addDoubleOr == 0 ) {
puts ( "|| (" );
addDoubleOr = 10 ;
} else {
puts ( " | (" );
}
changep -> lhsp () -> iterateAndNext ( * this );
if ( changep -> isWide ()) puts ( "[" + cvtToStr ( word ) + "]" );
puts ( " ^ " );
changep -> rhsp () -> iterateAndNext ( * this );
if ( changep -> isWide ()) puts ( "[" + cvtToStr ( word ) + "]" );
puts ( ")" );
}
}
}
V3OutCFile * newOutCFile ( AstModule * modp , bool slow , bool source , int filenum = 0 ) {
string filenameNoExt = v3Global . opt . makeDir () + "/" + modClassName ( modp );
if ( filenum ) filenameNoExt += "__" + cvtToStr ( filenum );
filenameNoExt += ( slow ? "__Slow" : "" );
V3OutCFile * ofp = NULL ;
if ( optSystemPerl ()) {
string filename = filenameNoExt + ".sp" ;
newCFile ( filename , slow , source );
ofp = new V3OutSpFile ( filename );
}
else if ( optSystemC ()) {
string filename = filenameNoExt + ( source ? ".cpp" : ".h" );
newCFile ( filename , slow , source );
ofp = new V3OutScFile ( filename );
}
else {
string filename = filenameNoExt + ( source ? ".cpp" : ".h" );
newCFile ( filename , slow , source );
ofp = new V3OutCFile ( filename );
}
ofp -> putsHeader ();
return ofp ;
}
//---------------------------------------
// VISITORS
virtual void visit ( AstCFunc * nodep , AstNUser * ) {
if ( nodep -> funcType () == AstCFuncType :: TRACE_INIT
|| nodep -> funcType () == AstCFuncType :: TRACE_FULL
|| nodep -> funcType () == AstCFuncType :: TRACE_CHANGE ) {
return ; // Handled specially
}
if ( ! ( nodep -> slow () ? m_slow : m_fast )) return ;
m_blkChangeDetVec . clear ();
m_splitSize += EmitCBaseCounterVisitor ( nodep ). count ();
puts ( " \n " );
puts ( nodep -> rtnTypeVoid ()); puts ( " " );
puts ( modClassName ( m_modp ) + "::" + nodep -> name ()
+ "(" + cFuncArgs ( nodep ) + ") { \n " );
2006-08-30 22:00:55 +00:00
puts ( "VL_DEBUG_IF(cout<< \" " );
2006-08-26 11:35:28 +00:00
for ( int i = 0 ; i < m_modp -> level (); i ++ ) { puts ( " " ); }
puts ( modClassName ( m_modp ) + "::" + nodep -> name ()
2006-08-30 22:00:55 +00:00
+ " \" <<endl; ); \n " );
2006-08-26 11:35:28 +00:00
2006-08-30 21:07:55 +00:00
if ( nodep -> symProlog ()) puts ( EmitCBaseVisitor :: symTopAssign () + " \n " );
2006-08-26 11:35:28 +00:00
if ( nodep -> initsp ()) puts ( "// Variables \n " );
2006-08-29 00:58:48 +00:00
ofp () -> putAlign ( V3OutFile :: AL_AUTO , 4 );
2006-08-26 11:35:28 +00:00
for ( AstNode * subnodep = nodep -> argsp (); subnodep ; subnodep = subnodep -> nextp ()) {
if ( AstVar * varp = subnodep -> castVar ()) {
if ( varp -> isFuncReturn ()) emitVarDecl ( varp , "" );
}
}
emitVarList ( nodep -> initsp (), EVL_ALL , "" );
2006-08-29 00:58:48 +00:00
ofp () -> putAlign ( V3OutFile :: AL_AUTO , 4 );
2006-08-26 11:35:28 +00:00
emitVarList ( nodep -> stmtsp (), EVL_ALL , "" );
2006-08-29 00:58:48 +00:00
ofp () -> putAlign ( V3OutFile :: AL_AUTO , 4 );
2006-08-26 11:35:28 +00:00
nodep -> initsp () -> iterateAndNext ( * this );
if ( nodep -> stmtsp ()) puts ( "// Body \n " );
nodep -> stmtsp () -> iterateAndNext ( * this );
#ifndef NEW_ORDERING
if ( ! m_blkChangeDetVec . empty ()) emitChangeDet ();
#endif
if ( nodep -> finalsp ()) puts ( "// Final \n " );
nodep -> finalsp () -> iterateAndNext ( * this );
//
if ( ! m_blkChangeDetVec . empty ()) puts ( "return __req; \n " );
// puts("__Vm_activity = true;\n");
puts ( "} \n " );
}
void emitChangeDet () {
puts ( "// Change detection \n " );
puts ( "IData __req = false; // Logically a bool \n " ); // But not because it results in faster code
bool gotOne = false ;
for ( vector < AstChangeDet *>:: iterator it = m_blkChangeDetVec . begin ();
it != m_blkChangeDetVec . end (); ++ it ) {
AstChangeDet * changep = * it ;
if ( changep -> lhsp ()) {
if ( ! gotOne ) { // Not a clocked block
puts ( "__req |= (" );
}
else puts ( " \n " );
doubleOrDetect ( changep , gotOne );
}
}
if ( gotOne ) {
puts ( "); \n " );
//puts("VL_DEBUG_IF( if (__req) cout<<\"\tCLOCKREQ );");
}
}
virtual void visit ( AstChangeDet * nodep , AstNUser * ) {
m_blkChangeDetVec . push_back ( nodep );
}
//---------------------------------------
// ACCESSORS
// METHODS
// Low level
void emitVarResets ( AstModule * modp );
void emitCellCtors ( AstModule * modp );
void emitSensitives ();
// Medium level
2006-08-30 17:27:53 +00:00
void emitCtorImp ( AstModule * modp );
void emitConfigureImp ( AstModule * modp );
2006-08-26 11:35:28 +00:00
void emitCoverageDecl ( AstModule * modp );
2006-08-30 01:14:29 +00:00
void emitCoverageImp ( AstModule * modp );
2006-08-30 17:27:53 +00:00
void emitDestructorImp ( AstModule * modp );
2006-08-26 11:35:28 +00:00
void emitTextSection ( AstType type );
void emitIntFuncDecls ( AstModule * modp );
// High level
void emitImp ( AstModule * modp );
void emitImpBottom ( AstModule * modp );
void emitStaticDecl ( AstModule * modp );
void emitWrapEval ( AstModule * modp );
void emitInt ( AstModule * modp );
void writeMakefile ( string filename );
public :
EmitCImp () {
m_modp = NULL ;
m_splitSize = 0 ;
m_splitFilenum = 0 ;
}
virtual ~ EmitCImp () {}
void main ( AstModule * modp , bool slow , bool fast );
void mainDoFunc ( AstCFunc * nodep ) {
nodep -> accept ( * this );
}
int splitSize () { return m_splitSize ; }
};
//######################################################################
// Internal EmitCStmts
void EmitCStmts :: emitVarDecl ( AstVar * nodep , const string & prefixIfImp ) {
if ( nodep -> isIO ()) {
if ( nodep -> isSc ()) {
m_ctorVarsVec . push_back ( nodep );
2006-08-29 00:58:48 +00:00
ofp () -> putAlign ( nodep -> isStatic (), 4 ); // sc stuff is a structure, so bigger alignment
2006-08-26 11:35:28 +00:00
if ( nodep -> attrScClocked () && nodep -> isInput ()) {
puts ( "sc_in_clk \t " );
} else {
2006-09-25 20:40:52 +00:00
if ( nodep -> isInout ()) puts ( "sc_inout<" );
2006-08-26 11:35:28 +00:00
else if ( nodep -> isInput ()) puts ( "sc_in<" );
else if ( nodep -> isOutput ()) puts ( "sc_out<" );
else nodep -> v3fatalSrc ( "Unknown type" );
2006-10-05 14:53:17 +00:00
puts ( nodep -> scType ());
2006-08-26 11:35:28 +00:00
puts ( "> \t " );
}
puts ( nodep -> name ());
puts ( "; \n " );
} else { // C++ signals
2006-08-29 00:58:48 +00:00
ofp () -> putAlign ( nodep -> isStatic (), nodep -> widthAlignBytes ());
2006-09-25 20:40:52 +00:00
if ( nodep -> isInout ()) puts ( "VL_INOUT" );
2006-08-26 11:35:28 +00:00
else if ( nodep -> isInput ()) puts ( "VL_IN" );
else if ( nodep -> isOutput ()) puts ( "VL_OUT" );
else nodep -> v3fatalSrc ( "Unknown type" );
if ( nodep -> isQuad ()) puts ( "64" );
else if ( nodep -> widthMin () <= 8 ) puts ( "8" );
else if ( nodep -> widthMin () <= 16 ) puts ( "16" );
if ( ! nodep -> isWide ())
puts ( "(" + nodep -> name ()
+ "," + cvtToStr ( nodep -> msb ())
+ "," + cvtToStr ( nodep -> lsb ()));
else puts ( "W(" + nodep -> name ()
+ "," + cvtToStr ( nodep -> msb ())
+ "," + cvtToStr ( nodep -> lsb ())
+ "," + cvtToStr ( nodep -> widthWords ()));
puts ( "); \n " );
}
} else {
// Arrays need a small alignment, but may need different padding after.
// For example three VL_SIG8's needs alignment 1 but size 3.
2006-08-29 00:58:48 +00:00
ofp () -> putAlign ( nodep -> isStatic (), nodep -> widthAlignBytes (), nodep -> arrayElements () * nodep -> widthAlignBytes ());
if ( nodep -> isStatic () && prefixIfImp == "" ) puts ( "static " );
2006-08-29 12:01:02 +00:00
if ( nodep -> isStatic ()) puts ( "VL_ST_" ); else puts ( "VL_" );
2006-08-26 11:35:28 +00:00
if ( nodep -> widthMin () <= 8 ) {
2006-08-29 12:01:02 +00:00
puts ( "SIG8(" );
2006-08-26 11:35:28 +00:00
} else if ( nodep -> widthMin () <= 16 ) {
2006-08-29 12:01:02 +00:00
puts ( "SIG16(" );
2006-08-26 11:35:28 +00:00
} else if ( nodep -> isQuad ()) {
2006-08-29 12:01:02 +00:00
puts ( "SIG64(" );
2006-08-26 11:35:28 +00:00
} else if ( ! nodep -> isWide ()) {
2006-08-29 12:01:02 +00:00
puts ( "SIG(" );
2006-08-26 11:35:28 +00:00
} else {
2006-08-29 12:01:02 +00:00
puts ( "SIGW(" );
2006-08-26 11:35:28 +00:00
}
if ( prefixIfImp != "" ) { puts ( prefixIfImp ); puts ( "::" ); }
puts ( nodep -> name ());
for ( AstRange * arrayp = nodep -> arraysp (); arrayp ; arrayp = arrayp -> nextp () -> castRange ()) {
puts ( "[" + cvtToStr ( arrayp -> elementsConst ()) + "]" );
}
puts ( "," + cvtToStr ( nodep -> msb ()) + "," + cvtToStr ( nodep -> lsb ()));
if ( nodep -> isWide ()) puts ( "," + cvtToStr ( nodep -> widthWords ()));
puts ( "); \n " );
}
}
void EmitCStmts :: emitVarCtors () {
ofp () -> indentInc ();
bool first = true ;
for ( vector < AstVar *>:: iterator it = m_ctorVarsVec . begin (); it != m_ctorVarsVec . end (); ++ it ) {
if ( first ) {
first = false ;
puts ( " \n " );
puts ( "#if (SYSTEMC_VERSION>20011000) \n " ); // SystemC 2.0.1 and newer
puts ( " : " );
}
else puts ( ", " );
if ( ofp () -> exceededWidth ()) puts ( " \n " );
puts (( * it ) -> name ()); puts ( "( \" " );
puts (( * it ) -> name ()); puts ( " \" )" );
}
if ( ! first ) puts ( " \n #endif \n " );
ofp () -> indentDec ();
}
bool EmitCStmts :: emitSimpleOk ( AstNodeMath * nodep ) {
// Can we put out a simple (A + B) instead of VL_ADD_III(A,B)?
if ( nodep -> emitSimpleOperator () == "" ) return false ;
if ( nodep -> isWide ()) return false ;
if ( nodep -> op1p ()) { if ( nodep -> op1p () -> isWide ()) return false ; }
if ( nodep -> op2p ()) { if ( nodep -> op2p () -> isWide ()) return false ; }
if ( nodep -> op3p ()) { if ( nodep -> op3p () -> isWide ()) return false ; }
return true ;
}
void EmitCStmts :: emitOpName ( AstNode * nodep , const string & opname ) {
putbs ( opname + "_" );
if ( nodep -> emitWordForm ()) {
emitIQW ( nodep -> op1p ());
puts ( "(" );
if ( nodep -> op1p () -> isWide ()) {
puts ( cvtToStr ( nodep -> op1p () -> widthWords ()));
puts ( ", " );
}
} else {
emitIQW ( nodep );
if ( nodep -> op1p ()) { emitIQW ( nodep -> op1p ()); }
if ( nodep -> op2p ()) { emitIQW ( nodep -> op2p ()); }
if ( nodep -> op3p ()) { emitIQW ( nodep -> op3p ()); }
puts ( "(" );
puts ( cvtToStr ( nodep -> widthMin ()));
if ( nodep -> op1p ()) { puts ( "," + cvtToStr ( nodep -> op1p () -> widthMin ())); }
if ( nodep -> op2p ()) { puts ( "," + cvtToStr ( nodep -> op2p () -> widthMin ())); }
if ( nodep -> op3p ()) { puts ( "," + cvtToStr ( nodep -> op3p () -> widthMin ())); }
if ( nodep -> op1p () || nodep -> isWide ()) puts ( ", " );
}
if ( nodep -> isWide ()) {
if ( ! m_wideTempRefp ) nodep -> v3fatalSrc ( "Wide Op w/ no temp, perhaps missing op in V3EmitC?" );
puts ( m_wideTempRefp -> hiername ());
puts ( m_wideTempRefp -> varp () -> name ());
m_wideTempRefp = NULL ;
if ( nodep -> op1p ()) puts ( ", " );
}
}
//----------------------------------------------------------------------
// Mid level - VISITS
// We only do one display at once, so can just use static state
struct EmitDispState {
bool m_wide ; // Put out a wide func that needs string buffer
string m_format ; // "%s" and text from user
vector < AstNode *> m_argsp ; // Each argument to be printed
vector < string > m_argsFunc ; // Function before each argument to be printed
EmitDispState () { clear (); }
void clear () {
m_wide = false ;
m_format = "" ;
m_argsp . clear ();
m_argsFunc . clear ();
}
void pushFormat ( const string & fmt ) { m_format += fmt ; }
void pushFormat ( char fmt ) { m_format += fmt ; }
void pushArg ( AstNode * nodep , const string & func ) {
m_argsp . push_back ( nodep ); m_argsFunc . push_back ( func );
}
} emitDispState ;
void EmitCStmts :: displayEmit ( AstDisplay * nodep ) {
if ( emitDispState . m_format != "" ) {
// Format
if ( nodep -> filep ()) {
puts ( "if (" );
nodep -> filep () -> iterate ( * this ); // Check if closed, to avoid core dump
puts ( ") fprintf(VL_CVT_Q_FP(" );
nodep -> filep () -> iterate ( * this );
puts ( "), \" " );
} else {
puts ( "VL_PRINTF( \" " );
}
ofp () -> putsNoTracking ( emitDispState . m_format );
puts ( " \" " );
// Arguments
for ( unsigned i = 0 ; i < emitDispState . m_argsp . size (); i ++ ) {
puts ( "," );
AstNode * argp = emitDispState . m_argsp [ i ];
string func = emitDispState . m_argsFunc [ i ];
ofp () -> indentInc ();
ofp () -> putbs ( "" );
if ( func != "" ) puts ( func );
if ( argp ) argp -> iterate ( * this );
if ( func != "" ) puts ( ")" );
ofp () -> indentDec ();
}
// End
puts ( "); \n " );
// Prep for next
emitDispState . clear ();
}
}
string EmitCStmts :: displayFormat ( AstNode * widthNodep , string in ,
char fmtLetter , bool padZero , bool reallyString ) {
if ( fmtLetter == 's' ) padZero = false ;
if ( widthNodep && widthNodep -> isWide ()
&& ( fmtLetter == 'd' || fmtLetter == 'u' )) {
widthNodep -> v3error ( "Unsupported: $display of dec format of > 64 bit results (use hex format instead)" );
}
if ( widthNodep && widthNodep -> widthMin () > 8 && fmtLetter == 'c' ) {
widthNodep -> v3error ( "$display of char format of > 8 bit result" );
}
string fmt ;
if ( in == "" ) {
// Size naturally
if ( widthNodep == NULL ) fmt = "" ; // Out of args, will get error
if ( fmtLetter == 'u' || fmtLetter == 'd' ) { // Decimal. Spec says leading spaces, not zeros
double mantissabits = widthNodep -> widthMin () - (( fmtLetter == 'd' ) ? 1 : 0 );
double maxval = pow ( 2.0 , mantissabits );
double dchars = log10 ( maxval ) + 1.0 ;
if ( fmtLetter == 'd' ) dchars ++ ; // space for sign
int nchars = int ( dchars );
fmt = cvtToStr ( nchars );
} else if ( fmtLetter != 's' && fmtLetter != 'c' ) { // Strings/chars don't get padding
int bitsPerChar = ( fmtLetter == 'b' ? 1 : fmtLetter == 'o' ? 3 : 4 );
int nchars = ( widthNodep -> widthMin () + bitsPerChar - 1 ) / bitsPerChar ;
if ( padZero ) fmt = ( string )( "0" ) + cvtToStr ( nchars );
else fmt = cvtToStr ( nchars );
}
} else if ( in == "0" ) {
fmt = "" ; // No width
} else {
fmt = in ;
}
return fmt ;
}
void EmitCStmts :: displayArg ( AstDisplay * dispp , AstNode ** elistp , string fmt , char fmtLetter ) {
// Print display argument, edits elistp
if ( !* elistp ) {
dispp -> v3error ( "Missing arguments for $display format" );
return ;
}
if (( * elistp ) -> widthMin () > VL_VALUE_STRING_MAX_WIDTH ) {
dispp -> v3error ( "Exceeded limit of 1024 bits for any display arguments" );
}
if (( * elistp ) -> isWide () // Have to use our own function for wide,
|| fmtLetter == 's' // ... Verilog strings
|| fmtLetter == 'b' // ... binary, no printf %b in C
|| fmtLetter == 'd' ) { // ... Signed decimal
// We use a single static string, so must only have one call per VL_PRINT
if ( emitDispState . m_wide ) { displayEmit ( dispp ); }
emitDispState . m_wide = true ;
string nfmt = displayFormat ( * elistp , fmt , fmtLetter , false , true );
string pfmt = "%" + nfmt + "s" ;
string func = "VL_VALUE_FORMATTED_" ;
func += (( * elistp ) -> isWide ()) ? "W(" : (( * elistp ) -> isQuad ()) ? "Q(" : "I(" ;
func += ( cvtToStr (( * elistp ) -> widthMin ())
+ ",'" + fmtLetter + "'"
+ "," + ( fmt == "0" ? "true" : "false" ) + "," );
emitDispState . pushFormat ( pfmt );
emitDispState . pushArg ( * elistp , func );
} else {
2006-09-20 13:24:18 +00:00
string func ;
2006-08-26 11:35:28 +00:00
string nfmt = displayFormat ( * elistp , fmt , fmtLetter , true , false );
2006-10-06 16:08:46 +00:00
// We don't need to check for fmtLetter=='d', as it is above.
2006-09-20 13:24:18 +00:00
if (( * elistp ) -> isQuad () && ( fmtLetter == 'u' || fmtLetter == 'o' || fmtLetter == 'x' )) {
nfmt += "ll" ;
func = "(unsigned long long)(" ; // Must match %ull to avoid warnings
}
2006-08-26 11:35:28 +00:00
string pfmt = "%" + nfmt + fmtLetter ;
2006-09-20 13:24:18 +00:00
2006-08-26 11:35:28 +00:00
emitDispState . pushFormat ( pfmt );
2006-09-20 13:24:18 +00:00
emitDispState . pushArg ( * elistp , func );
2006-08-26 11:35:28 +00:00
}
// Next parameter
* elistp = ( * elistp ) -> nextp ();
}
void EmitCStmts :: visit ( AstDisplay * nodep , AstNUser * ) {
string vformat = nodep -> text ();
bool addNewline = ( nodep -> newline () == '\n' );
AstNode * elistp = nodep -> exprsp ();
// Convert Verilog display to C printf formats
// "%0t" becomes "%d"
emitDispState . clear ();
string fmt = "" ;
string :: iterator pos = vformat . begin ();
if ( * pos == '"' ) pos ++ ;
bool inPct = false ;
for (; pos != vformat . end (); ++ pos ) {
if ( pos [ 0 ] == '"' && ( pos + 1 ) == vformat . end ()) break ;
if ( inPct && pos [ 0 ] == '%' ) {
emitDispState . pushFormat ( "%%" ); // We're printf'ing it, so need to quote the %
inPct = false ;
} else if ( pos [ 0 ] == '%' ) {
inPct = true ;
fmt = "" ;
} else if ( ! inPct ) { // Normal text
//char text[2]; text[0]=*pos; text[1]='\0';
emitDispState . pushFormat ( * pos );
} else { // Format character
if ( isdigit ( pos [ 0 ])) {
// Digits, like %5d, etc.
fmt += pos [ 0 ];
} else {
inPct = false ;
switch ( tolower ( pos [ 0 ])) {
// Special codes
case '~' : displayArg ( nodep , & elistp , fmt , 'd' ); break ; // Signed decimal
// Spec: h d o b c l
case 'b' : displayArg ( nodep , & elistp , fmt , 'b' ); break ;
case 'c' : displayArg ( nodep , & elistp , fmt , 'c' ); break ;
case 'd' : displayArg ( nodep , & elistp , fmt , 'u' ); break ; // Unsigned decimal
case 'o' : displayArg ( nodep , & elistp , fmt , 'o' ); break ;
case 'h' :
case 'x' : displayArg ( nodep , & elistp , fmt , 'x' ); break ;
case 's' : displayArg ( nodep , & elistp , fmt , 's' ); break ;
case 't' : displayArg ( nodep , & elistp , fmt , 'u' ); break ;
case 'm' : {
emitDispState . pushFormat ( "%s" );
2006-08-30 22:00:55 +00:00
emitDispState . pushArg ( NULL , "vlSymsp->name(" );
2006-08-26 11:35:28 +00:00
for ( AstText * textp = nodep -> scopeTextp (); textp ; textp = textp -> nextp () -> castText ()) {
emitDispState . pushFormat ( textp -> text ());
}
break ;
}
case 'u' :
case 'z' :
case 'l' :
case 'v' :
nodep -> v3error ( "Unsupported: $display format code: %" << pos [ 0 ]);
break ;
default :
nodep -> v3error ( "Unknown $display format code: %" << pos [ 0 ]);
break ;
}
}
}
}
if ( elistp != NULL ) {
nodep -> v3error ( "Extra arguments for $display format \n " );
}
if ( addNewline ) emitDispState . pushFormat ( " \\ n" );
displayEmit ( nodep );
}
//######################################################################
// Internal EmitC
void EmitCImp :: emitVarResets ( AstModule * modp ) {
puts ( "// Reset internal values \n " );
if ( modp -> isTop ()) {
2006-08-29 00:27:04 +00:00
if ( v3Global . opt . inhibitSim ()) puts ( "__Vm_inhibitSim = false; \n " );
2006-08-26 11:35:28 +00:00
puts ( " \n " );
}
puts ( "// Reset structure values \n " );
for ( AstNode * nodep = modp -> stmtsp (); nodep ; nodep = nodep -> nextp ()) {
if ( AstVar * varp = nodep -> castVar ()) {
if ( varp -> isIO () && modp -> isTop () && optSystemC ()) {
// System C top I/O doesn't need loading, as the lower level subinst code does it.
}
else if ( varp -> isParam ()) {
if ( ! varp -> hasSimpleInit ()) nodep -> v3fatalSrc ( "No init for a param?" );
//puts("// parameter "+varp->name()+" = "+varp->initp()->name()+"\n");
}
else if ( AstInitArray * initarp = varp -> initp () -> castInitArray ()) {
AstConst * constsp = initarp -> initsp () -> castConst ();
if ( ! varp -> arraysp ()) varp -> v3fatalSrc ( "InitArray under non-arrayed var" );
for ( int i = 0 ; i < varp -> arraysp () -> elementsConst (); i ++ ) {
if ( ! constsp ) initarp -> v3fatalSrc ( "Not enough values in array initalizement" );
emitSetVarConstant ( varp -> name () + "[" + cvtToStr ( i ) + "]" , constsp );
constsp = constsp -> nextp () -> castConst ();
}
}
else {
int vects = 0 ;
for ( AstRange * arrayp = varp -> arraysp (); arrayp ; arrayp = arrayp -> nextp () -> castRange ()) {
int vecnum = vects ++ ;
if ( arrayp -> msbConst () < arrayp -> lsbConst ()) varp -> v3fatalSrc ( "Should have swapped msb & lsb earlier." );
string ivar = string ( "__Vi" ) + cvtToStr ( vecnum );
// MSVC++ pre V7 doesn't support 'for (int ...)', so declare in sep block
puts ( "{ int __Vi" + cvtToStr ( vecnum ) + "=" + cvtToStr ( 0 ) + ";" );
puts ( " for (; " + ivar + "<" + cvtToStr ( arrayp -> elementsConst ()));
puts ( "; ++" + ivar + ") { \n " );
}
bool zeroit = ( varp -> attrFileDescr () // Zero it out, so we don't core dump if never call $fopen
|| ( varp -> name (). c_str ()[ 0 ] == '_' && v3Global . opt . underlineZero ()));
if ( varp -> isWide ()) {
// DOCUMENT: We randomize everything. If the user wants a _var to be zero,
// there should be a initial statement. (Different from verilator2.)
if ( zeroit ) puts ( "VL_ZERO_RESET_W(" );
else puts ( "VL_RAND_RESET_W(" );
puts ( cvtToStr ( varp -> widthMin ()));
puts ( "," );
puts ( varp -> name ());
for ( int v = 0 ; v < vects ; ++ v ) puts ( "[__Vi" + cvtToStr ( v ) + "]" );
puts ( "); \n " );
} else {
puts ( varp -> name ());
for ( int v = 0 ; v < vects ; ++ v ) puts ( "[__Vi" + cvtToStr ( v ) + "]" );
if ( zeroit ) {
puts ( "= 0; \n " );
} else {
puts ( " = VL_RAND_RESET_" );
emitIQW ( varp );
puts ( "(" );
puts ( cvtToStr ( varp -> widthMin ()));
puts ( "); \n " );
}
}
for ( int v = 0 ; v < vects ; ++ v ) puts ( "}} \n " );
}
}
}
}
void EmitCImp :: emitCoverageDecl ( AstModule * modp ) {
m_coverIds . clear ();
for ( AstNode * nodep = modp -> stmtsp (); nodep ; nodep = nodep -> nextp ()) {
if ( AstCoverDecl * declp = nodep -> castCoverDecl ()) {
m_coverIds . remap ( declp );
}
}
if ( m_coverIds . size ()) {
ofp () -> putsPrivate ( true );
puts ( "// Coverage \n " );
puts ( "SpZeroed<uint32_t> \t __Vcoverage[" ); puts ( cvtToStr ( m_coverIds . size ())); puts ( "]; \n " );
2006-08-29 00:58:48 +00:00
ofp () -> putAlign ( V3OutFile :: AL_AUTO , sizeof ( uint32_t ) * m_coverIds . size ());
2006-08-30 01:14:29 +00:00
puts ( "void __vlCoverInsert(SpZeroed<uint32_t>* countp, const char* filename, int lineno, int column, \n " );
puts ( "const char* hier, const char* type, const char* comment); \n " );
}
}
2006-08-30 17:27:53 +00:00
void EmitCImp :: emitCtorImp ( AstModule * modp ) {
puts ( " \n " );
if ( optSystemPerl () && modp -> isTop ()) {
puts ( "SP_CTOR_IMP(" + modClassName ( modp ) + ")" );
} else if ( optSystemC () && modp -> isTop ()) {
puts ( "VL_SC_CTOR_IMP(" + modClassName ( modp ) + ")" );
} else {
puts ( "VL_CTOR_IMP(" + modClassName ( modp ) + ")" );
}
emitVarCtors ();
puts ( " { \n " );
emitCellCtors ( modp );
emitSensitives ();
emitVarResets ( modp );
emitTextSection ( AstType :: SCCTOR );
if ( optSystemPerl ()) puts ( "SP_AUTO_CTOR; \n " );
puts ( "} \n " );
}
void EmitCImp :: emitConfigureImp ( AstModule * modp ) {
puts ( " \n void " + modClassName ( modp ) + "::__Vconfigure(" + symClassName () + "* symsp) { \n " );
puts ( "__VlSymsp = symsp; \n " ); // First, as later stuff needs it.
bool first = true ;
for ( AstNode * nodep = modp -> stmtsp (); nodep ; nodep = nodep -> nextp ()) {
if ( nodep -> castCoverDecl ()) {
if ( first ) {
first = false ;
puts ( "// Coverage Declarations \n " );
}
nodep -> accept ( * this );
}
}
puts ( "} \n " );
}
2006-08-30 01:14:29 +00:00
void EmitCImp :: emitCoverageImp ( AstModule * modp ) {
if ( m_coverIds . size ()) {
puts ( " \n // Coverage \n " );
2006-08-26 11:35:28 +00:00
// Rather then putting out SP_COVER_INSERT calls directly, we do it via this function
// This gets around gcc slowness constructing all of the template arguments
2006-08-30 01:14:29 +00:00
puts ( "void " + modClassName ( m_modp ) + "::__vlCoverInsert(SpZeroed<uint32_t>* countp, const char* filename, int lineno, int column, \n " );
2006-08-26 11:35:28 +00:00
puts ( "const char* hier, const char* type, const char* comment) { \n " );
puts ( "SP_COVER_INSERT(countp," );
puts ( " \" filename \" ,filename," );
puts ( " \" lineno \" ,lineno," );
puts ( " \" column \" ,column, \n " );
2006-08-30 01:14:29 +00:00
//puts( "\"hier\",string(__VlSymsp->name())+hier,"); // Need to move hier into scopes and back out if do this
2006-08-26 11:35:28 +00:00
puts ( " \" hier \" ,string(name())+hier," );
puts ( " \" type \" ,type," );
puts ( " \" comment \" ,comment); \n " );
puts ( "} \n " );
}
}
2006-08-30 17:27:53 +00:00
void EmitCImp :: emitDestructorImp ( AstModule * modp ) {
puts ( " \n " );
puts ( modClassName ( modp ) + "::~" + modClassName ( modp ) + "() { \n " );
emitTextSection ( AstType :: SCDTOR );
if ( modp -> isTop ()) puts ( "delete __VlSymsp; __VlSymsp=NULL; \n " );
puts ( "} \n " );
}
2006-08-26 11:35:28 +00:00
void EmitCImp :: emitStaticDecl ( AstModule * modp ) {
// Need implementation here. Be careful of alignment code; needs to be uniquified
// with module name to avoid multiple symbols.
//emitVarList(modp->stmtsp(), EVL_ALL, modp->name());
}
void EmitCImp :: emitTextSection ( AstType type ) {
int last_line = - 999 ;
for ( AstNode * nodep = m_modp -> stmtsp (); nodep != NULL ; nodep = nodep -> nextp ()) {
if ( AstNodeText * textp = nodep -> castNodeText ()) {
if ( nodep -> type () == type ) {
if ( last_line != nodep -> fileline () -> lineno ()) {
if ( last_line < 0 ) {
puts ( " \n //*** Below code from `systemc in Verilog file \n " );
}
ofp () -> putsNoTracking ( "//#line " + cvtToStr ( nodep -> fileline () -> lineno ())
+ " \" " + nodep -> fileline () -> filename () + " \"\n " );
last_line = nodep -> fileline () -> lineno ();
}
ofp () -> putsNoTracking ( textp -> text ());
last_line ++ ;
}
}
}
if ( last_line > 0 ) {
puts ( "//*** Above code from `systemc in Verilog file \n\n " );
}
}
void EmitCImp :: emitCellCtors ( AstModule * modp ) {
if ( modp -> isTop ()) {
2006-08-30 01:14:29 +00:00
// Must be before other constructors, as __vlCoverInsert calls it
2006-08-30 21:07:55 +00:00
puts ( EmitCBaseVisitor :: symClassVar () + " = __VlSymsp = new " + symClassName () + "(this, name()); \n " );
puts ( EmitCBaseVisitor :: symTopAssign () + " \n " );
2006-08-26 11:35:28 +00:00
}
for ( AstNode * nodep = modp -> stmtsp (); nodep ; nodep = nodep -> nextp ()) {
if ( AstCell * cellp = nodep -> castCell ()) {
puts ( "VL_CELL (" + cellp -> name () + ", " + modClassName ( cellp -> modp ()) + "); \n " );
}
}
}
void EmitCImp :: emitSensitives () {
// Create sensitivity list for when to evaluate the model.
// If C++ code, the user must call this routine themself.
if ( m_modp -> isTop () && optSystemC ()) {
puts ( "// Sensitivities on all clocks and combo inputs \n " );
puts ( "SC_METHOD(eval); \n " );
for ( AstNode * nodep = m_modp -> stmtsp (); nodep ; nodep = nodep -> nextp ()) {
if ( AstVar * varp = nodep -> castVar ()) {
if ( varp -> isInput () && ( varp -> isScSensitive () || varp -> isUsedClock ())) {
puts ( "sensitive(" + varp -> name () + "); \n " );
}
}
}
puts ( " \n " );
}
}
void EmitCImp :: emitWrapEval ( AstModule * modp ) {
puts ( " \n void " + modClassName ( modp ) + "::eval() { \n " );
2006-08-30 21:07:55 +00:00
puts ( EmitCBaseVisitor :: symClassVar () + " = this->__VlSymsp; // Setup global symbol table \n " );
puts ( EmitCBaseVisitor :: symTopAssign () + " \n " );
2006-08-26 11:35:28 +00:00
puts ( "// Initialize \n " );
2006-08-30 21:07:55 +00:00
puts ( "if (VL_UNLIKELY(!vlSymsp->__Vm_didInit)) _eval_initial_loop(vlSymsp); \n " );
2006-08-29 00:27:04 +00:00
if ( v3Global . opt . inhibitSim ()) {
2006-08-30 21:07:55 +00:00
puts ( "if (VL_UNLIKELY(__Vm_inhibitSim)) return; \n " );
2006-08-29 00:27:04 +00:00
}
2006-08-26 11:35:28 +00:00
puts ( "// Evaluate till stable \n " );
puts ( "VL_DEBUG_IF(cout<< \"\\ n----TOP Evaluate " + modClassName ( modp ) + "::eval \" <<endl; ); \n " );
#ifndef NEW_ORDERING
puts ( "int __VclockLoop = 0; \n " );
2006-08-30 21:07:55 +00:00
puts ( "IData __Vchange=1; \n " );
puts ( "while (VL_LIKELY(__Vchange)) { \n " );
2006-08-26 11:35:28 +00:00
puts ( "VL_DEBUG_IF(cout<< \" Clock loop \" <<endl;); \n " );
#endif
2006-08-30 21:07:55 +00:00
puts ( "vlSymsp->__Vm_activity = true; \n " );
puts ( "_eval(vlSymsp); \n " );
2006-08-26 11:35:28 +00:00
#ifndef NEW_ORDERING
2006-08-30 21:07:55 +00:00
puts ( "__Vchange = _change_request(vlSymsp); \n " );
2006-08-26 11:35:28 +00:00
puts ( "if (++__VclockLoop > 100) vl_fatal(__FILE__,__LINE__,__FILE__, \" Verilated model didn't converge \" ); \n " );
puts ( "} \n " );
#endif
puts ( "} \n " );
//
2006-08-30 21:07:55 +00:00
puts ( " \n void " + modClassName ( modp ) + "::_eval_initial_loop(" + EmitCBaseVisitor :: symClassVar () + ") { \n " );
puts ( "vlSymsp->__Vm_didInit = true; \n " );
puts ( "_eval_initial(vlSymsp); \n " );
2006-08-26 11:35:28 +00:00
#ifndef NEW_ORDERING
2006-08-30 21:07:55 +00:00
puts ( "vlSymsp->__Vm_activity = true; \n " );
2006-08-26 11:35:28 +00:00
puts ( "int __VclockLoop = 0; \n " );
2006-08-30 21:07:55 +00:00
puts ( "IData __Vchange=1; \n " );
puts ( "while (VL_LIKELY(__Vchange)) { \n " );
2006-08-26 11:35:28 +00:00
#endif
2006-08-30 21:07:55 +00:00
puts ( "_eval_settle(vlSymsp); \n " );
puts ( "_eval(vlSymsp); \n " );
2006-08-26 11:35:28 +00:00
#ifndef NEW_ORDERING
2006-08-30 21:07:55 +00:00
puts ( "__Vchange = _change_request(vlSymsp); \n " );
2006-08-26 11:35:28 +00:00
puts ( "if (++__VclockLoop > 100) vl_fatal(__FILE__,__LINE__,__FILE__, \" Verilated model didn't DC converge \" ); \n " );
puts ( "} \n " );
#endif
puts ( "} \n " );
}
//----------------------------------------------------------------------
// Top interface/ implementation
void EmitCStmts :: emitVarList ( AstNode * firstp , EisWhich which , const string & prefixIfImp ) {
// Put out a list of signal declarations
2006-09-19 15:27:15 +00:00
// in order of 0:clocks, 1:vluint8, 2:vluint16, 4:vluint32, 5:vluint64, 6:wide, 7:arrays
2006-08-26 11:35:28 +00:00
// This aids cache packing and locality
2006-08-29 00:58:48 +00:00
// Largest->smallest reduces the number of pad variables.
// But for now, Smallest->largest makes it more likely a small offset will allow access to the signal.
for ( int isstatic = 1 ; isstatic >= 0 ; isstatic -- ) {
if ( prefixIfImp != "" && ! isstatic ) continue ;
for ( int size = 0 ; size < 8 ; size ++ ) {
if ( size == 3 ) continue ;
for ( AstNode * nodep = firstp ; nodep ; nodep = nodep -> nextp ()) {
if ( AstVar * varp = nodep -> castVar ()) {
bool doit = true ;
switch ( which ) {
case EVL_ALL : doit = true ; break ;
case EVL_IO : doit = varp -> isIO (); break ;
case EVL_SIG : doit = ( varp -> isSignal () && ! varp -> isIO ()); break ;
case EVL_TEMP : doit = varp -> isTemp (); break ;
default : v3fatalSrc ( "Bad Case" );
}
if ( varp -> isStatic () ? ! isstatic : isstatic ) doit = false ;
if ( doit ) {
int sigbytes = varp -> widthAlignBytes ();
if ( varp -> isUsedClock () && varp -> widthMin () == 1 ) sigbytes = 0 ;
else if ( varp -> arraysp ()) sigbytes = 7 ;
else if ( varp -> isScWide ()) sigbytes = 6 ;
else if ( sigbytes == 8 ) sigbytes = 5 ;
else if ( sigbytes == 4 ) sigbytes = 4 ;
else if ( sigbytes == 2 ) sigbytes = 2 ;
else if ( sigbytes == 1 ) sigbytes = 1 ;
if ( size == sigbytes ) {
emitVarDecl ( varp , prefixIfImp );
}
2006-08-26 11:35:28 +00:00
}
}
}
}
2006-08-29 00:58:48 +00:00
ofp () -> putAlign ( isstatic , 4 , 0 , prefixIfImp . c_str ());
2006-08-26 11:35:28 +00:00
}
}
struct CmpName {
inline bool operator () ( const AstNode * lhsp , const AstNode * rhsp ) const {
return lhsp -> name () < rhsp -> name ();
}
};
void EmitCImp :: emitIntFuncDecls ( AstModule * modp ) {
vector < AstCFunc *> funcsp ;
for ( AstNode * nodep = modp -> stmtsp (); nodep ; nodep = nodep -> nextp ()) {
if ( AstCFunc * funcp = nodep -> castCFunc ()) {
if ( ! funcp -> skipDecl ()) {
funcsp . push_back ( funcp );
}
}
}
sort ( funcsp . begin (), funcsp . end (), CmpName ());
for ( vector < AstCFunc *>:: iterator it = funcsp . begin (); it != funcsp . end (); ++ it ) {
AstCFunc * funcp = * it ;
ofp () -> putsPrivate ( funcp -> declPrivate ());
2006-08-29 12:01:02 +00:00
if ( funcp -> isStatic ()) puts ( "static " );
2006-08-26 11:35:28 +00:00
puts ( funcp -> rtnTypeVoid ()); puts ( " \t " );
puts ( funcp -> name ()); puts ( "(" + cFuncArgs ( funcp ) + "); \n " );
}
}
void EmitCImp :: emitInt ( AstModule * modp ) {
// Always have this first; gcc has short circuiting if #ifdef is first in a file
if ( ! optSystemPerl ()) { // else done for us automatically
puts ( "#ifndef _" + modClassName ( modp ) + "_H_ \n " );
puts ( "#define _" + modClassName ( modp ) + "_H_ \n " );
puts ( " \n " );
}
ofp () -> putsIntTopInclude ();
puts ( "#include \" verilated.h \"\n " );
if ( v3Global . opt . coverage ()) {
puts ( "#include \" SpCoverage.h \"\n " );
}
// Declare foreign instances up front to make C++ happy
puts ( "class " + symClassName () + "; \n " );
for ( AstNode * nodep = modp -> stmtsp (); nodep ; nodep = nodep -> nextp ()) {
if ( AstCell * cellp = nodep -> castCell ()) {
puts ( "class " + modClassName ( cellp -> modp ()) + "; \n " );
}
}
puts ( " \n //---------- \n\n " );
emitTextSection ( AstType :: SCHDR );
if ( optSystemC () && modp -> isTop ()) {
puts ( "SC_MODULE(" + modClassName ( modp ) + ") { \n " );
} else {
puts ( "VL_MODULE(" + modClassName ( modp ) + ") { \n " );
}
if ( optSystemPerl ()) puts ( "/*AUTOATTR(verilated)*/ \n\n " );
ofp () -> resetPrivate ();
ofp () -> putsPrivate ( false ); // public:
// Instantiated modules
if ( optSystemPerl ()) {
puts ( "/*AUTOSUBCELLS*/ \n\n " );
} else {
puts ( "// CELLS \n " );
for ( AstNode * nodep = modp -> stmtsp (); nodep ; nodep = nodep -> nextp ()) {
if ( AstCell * cellp = nodep -> castCell ()) {
ofp () -> putsCellDecl ( modClassName ( cellp -> modp ()), cellp -> name ());
}
}
}
puts ( " \n // PORTS \n " );
emitVarList ( modp -> stmtsp (), EVL_IO , "" );
puts ( " \n // LOCAL SIGNALS \n " );
emitVarList ( modp -> stmtsp (), EVL_SIG , "" );
puts ( " \n // LOCAL VARIABLES \n " );
emitVarList ( modp -> stmtsp (), EVL_TEMP , "" );
puts ( " \n // INTERNAL VARIABLES \n " );
ofp () -> putsPrivate ( ! modp -> isTop ()); // private: unless top
2006-08-29 00:58:48 +00:00
ofp () -> putAlign ( V3OutFile :: AL_AUTO , 8 );
2006-08-26 11:35:28 +00:00
puts ( symClassName () + "* \t __VlSymsp; \t\t // Symbol table \n " );
ofp () -> putsPrivate ( false ); // public:
if ( modp -> isTop ()) {
2006-08-29 00:27:04 +00:00
if ( v3Global . opt . inhibitSim ()) {
2006-08-29 00:58:48 +00:00
ofp () -> putAlign ( V3OutFile :: AL_AUTO , sizeof ( bool ));
2006-08-29 00:27:04 +00:00
puts ( "bool \t __Vm_inhibitSim; \t ///< Set true to disable evaluation of module \n " );
}
2006-08-26 11:35:28 +00:00
}
2006-08-29 00:58:48 +00:00
ofp () -> putAlign ( V3OutFile :: AL_AUTO , 8 );
2006-08-26 11:35:28 +00:00
emitCoverageDecl ( modp ); // may flip public/private
2006-08-29 00:58:48 +00:00
ofp () -> putAlign ( V3OutFile :: AL_AUTO , 8 );
2006-08-26 11:35:28 +00:00
puts ( " \n // PARAMETERS \n " );
ofp () -> putsPrivate ( false ); // public:
for ( AstNode * nodep = modp -> stmtsp (); nodep ; nodep = nodep -> nextp ()) {
if ( AstVar * varp = nodep -> castVar ()) {
if ( varp -> isParam () && ( varp -> isUsedParam () || varp -> isSigPublic ())) {
if ( ! varp -> initp ()) nodep -> v3fatalSrc ( "No init for a param?" );
// These should be static const values, however microsloth VC++ doesn't
// support them. They also cause problems with GDB under GCC2.95.
if ( varp -> isScWide ()) { // Unsupported for output
puts ( "// enum WData " + varp -> name () + " //wide" );
} else if ( ! varp -> initp () -> castConst ()) { // Unsupported for output
puts ( "// enum IData " + varp -> name () + " //not simple value" );
} else {
puts ( "enum " );
puts ( varp -> isQuad () ? "_QData" : "_IData" );
puts ( "" + varp -> name () + " { " + varp -> name () + " = " );
varp -> initp () -> iterateAndNext ( * this );
puts ( "};" );
}
puts ( " \n " );
}
}
}
puts ( " \n // METHODS \n " );
ofp () -> resetPrivate ();
2006-08-30 21:07:55 +00:00
// We don't need a private copy constructor, as VerilatedModule has one for us.
ofp () -> putsPrivate ( true );
puts ( modClassName ( modp ) + "& operator= (const " + modClassName ( modp ) + "&); \t ///< Copying not allowed \n " );
puts ( modClassName ( modp ) + "(const " + modClassName ( modp ) + "&); \t ///< Copying not allowed \n " );
2006-08-26 11:35:28 +00:00
ofp () -> putsPrivate ( false ); // public:
if ( optSystemC () && modp -> isTop ()) {
puts ( "SC_CTOR(" + modClassName ( modp ) + "); \n " );
2006-08-30 17:27:53 +00:00
puts ( "virtual ~" + modClassName ( modp ) + "(); \n " );
2006-08-26 11:35:28 +00:00
} else if ( optSystemC ()) {
puts ( "VL_CTOR(" + modClassName ( modp ) + "); \n " );
2006-08-30 17:27:53 +00:00
puts ( "~" + modClassName ( modp ) + "(); \n " );
2006-08-26 11:35:28 +00:00
} else {
puts ( modClassName ( modp ) + "(const char* name= \" TOP \" ); \n " );
2006-08-30 17:27:53 +00:00
puts ( "~" + modClassName ( modp ) + "(); \n " );
2006-08-26 11:35:28 +00:00
if ( v3Global . opt . trace ()) {
puts ( "void \t trace (SpTraceVcdCFile* tfp, int levels, int options=0); \n " );
}
}
2006-08-30 01:14:29 +00:00
puts ( "void \t __Vconfigure(" + symClassName () + "* symsp); \n " );
2006-08-26 11:35:28 +00:00
if ( optSystemPerl ()) puts ( "/*AUTOMETHODS*/ \n " );
emitTextSection ( AstType :: SCINT );
puts ( " \n // Sensitivity blocks \n " );
if ( modp -> isTop ()) {
puts ( "void \t final(); \t ///< Function to call when simulation completed \n " );
if ( optSystemC ()) ofp () -> putsPrivate ( true ); ///< eval() is invoked by our sensitive() calls.
puts ( "void \t eval(); \t ///< Main function to call from calling app when inputs change \n " );
2006-08-29 00:27:04 +00:00
if ( v3Global . opt . inhibitSim ()) {
puts ( "void \t inhibitSim(bool flag) { __Vm_inhibitSim=flag; } \t ///< Set true to disable evaluation of module \n " );
}
2006-08-26 11:35:28 +00:00
ofp () -> putsPrivate ( true ); // private:
2006-08-30 22:00:55 +00:00
puts ( "static void _eval_initial_loop(" + EmitCBaseVisitor :: symClassVar () + "); \n " );
2006-08-26 11:35:28 +00:00
}
emitIntFuncDecls ( modp );
if ( ! optSystemPerl () && v3Global . opt . trace ()) {
ofp () -> putsPrivate ( false ); // public:
2006-08-30 22:00:55 +00:00
puts ( "static void traceInit (SpTraceVcd* vcdp, void* userthis, uint32_t code); \n " );
puts ( "static void traceFull (SpTraceVcd* vcdp, void* userthis, uint32_t code); \n " );
puts ( "static void traceChg (SpTraceVcd* vcdp, void* userthis, uint32_t code); \n " );
2006-08-26 11:35:28 +00:00
}
2006-08-30 22:00:55 +00:00
puts ( "} VL_ATTR_ALIGNED(64); \n " );
2006-08-26 11:35:28 +00:00
puts ( " \n " );
// finish up h-file
if ( ! optSystemPerl ()) {
puts ( "#endif /*guard*/ \n " );
}
}
//----------------------------------------------------------------------
void EmitCImp :: emitImp ( AstModule * modp ) {
if ( optSystemPerl ()) {
puts ( "//############################################################ \n " );
puts ( "#sp implementation \n " );
}
ofp () -> printf ( "#include \" %-20s // For This \n " ,
( modClassName ( modp ) + ".h \" " ). c_str ());
// Us
2006-08-30 21:07:55 +00:00
puts ( "#include \" " + symClassName () + ".h \"\n " );
2006-08-26 11:35:28 +00:00
if ( optSystemPerl () && ( m_splitFilenum || ! m_fast )) {
puts ( " \n " );
puts ( "SP_MODULE_CONTINUED(" + modClassName ( modp ) + "); \n " );
}
emitTextSection ( AstType :: SCIMPHDR );
2006-08-29 00:58:48 +00:00
if ( m_slow && m_splitFilenum == 0 ) {
puts ( " \n //-------------------- \n " );
puts ( "// STATIC VARIABLES \n\n " );
emitVarList ( modp -> stmtsp (), EVL_ALL , modClassName ( modp ));
}
2006-08-26 11:35:28 +00:00
if ( m_fast && m_splitFilenum == 0 ) {
emitTextSection ( AstType :: SCIMP );
emitStaticDecl ( modp );
}
if ( m_slow && m_splitFilenum == 0 ) {
puts ( " \n //-------------------- \n " );
2006-08-30 17:27:53 +00:00
emitCtorImp ( modp );
emitConfigureImp ( modp );
emitDestructorImp ( modp );
2006-08-30 01:14:29 +00:00
emitCoverageImp ( modp );
2006-08-26 11:35:28 +00:00
}
2006-08-30 17:27:53 +00:00
2006-08-26 11:35:28 +00:00
if ( m_fast && m_splitFilenum == 0 ) {
if ( modp -> isTop ()) {
emitStaticDecl ( modp );
puts ( " \n //-------------------- \n " );
puts ( " \n " );
emitWrapEval ( modp );
}
}
if ( m_fast && m_splitFilenum == 0 ) {
if ( v3Global . opt . trace () && optSystemC () && m_modp -> isTop ()) {
puts ( " \n " );
puts ( " \n /*AUTOTRACE(__MODULE__,recurse,activity,exists)*/ \n\n " );
}
}
// Blocks
puts ( " \n //-------------------- \n " );
puts ( "// Internal Methods \n " );
}
void EmitCImp :: emitImpBottom ( AstModule * modp ) {
}
//######################################################################
void EmitCImp :: main ( AstModule * modp , bool slow , bool fast ) {
// Output a module
m_modp = modp ;
m_slow = slow ;
m_fast = fast ;
string filenameNoExt = v3Global . opt . makeDir () + "/" + modClassName ( modp ) + ( m_fast ? "" : "__Slow" );
if ( debug () >= 5 ) {
for ( int i = 0 ; i < modp -> level (); i ++ ) { cout << " " ; }
UINFONL ( 0 , " Emitting " << modClassName ( modp ) << endl );
}
if ( optSystemPerl ()) {
m_ofp = newOutCFile ( modp , ! m_fast , true );
if ( m_fast ) {
puts ( "#sp interface \n " );
emitInt ( modp );
}
}
else if ( optSystemC ()) {
if ( m_fast ) {
m_ofp = newOutCFile ( modp , ! m_fast , false /*source*/ );
emitInt ( modp );
delete m_ofp ; m_ofp = NULL ;
}
m_ofp = newOutCFile ( modp , ! m_fast , true /*source*/ );
}
else {
if ( m_fast ) {
m_ofp = newOutCFile ( modp , ! m_fast , false /*source*/ );
emitInt ( modp );
delete m_ofp ; m_ofp = NULL ;
}
m_ofp = newOutCFile ( modp , ! m_fast , true /*source*/ );
}
emitImp ( modp );
for ( AstNode * nodep = modp -> stmtsp (); nodep ; nodep = nodep -> nextp ()) {
if ( AstCFunc * funcp = nodep -> castCFunc ()) {
if ( v3Global . opt . outputSplit () > 1 && splitSize ()
&& v3Global . opt . outputSplit () < splitSize ()) {
// Close old file
emitImpBottom ( modp );
delete m_ofp ; m_ofp = NULL ;
// Open a new file
m_splitSize = 0 ;
m_ofp = newOutCFile ( modp , ! m_fast , true /*source*/ , ++ m_splitFilenum );
emitImp ( modp );
}
mainDoFunc ( funcp );
}
}
emitImpBottom ( modp );
delete m_ofp ; m_ofp = NULL ;
}
//######################################################################
// Tracing routines
class EmitCTrace : EmitCStmts {
AstCFunc * m_funcp ; // Function we're in now
bool m_slow ; // Making slow file
// METHODS
void emitTraceHeader () {
// Includes
if ( optSystemC ()) {
puts ( "#include \" SpTraceVcd.h \"\n " );
}
puts ( "#include \" SpTraceVcdC.h \"\n " );
puts ( "#include \" " + symClassName () + ".h \"\n " );
puts ( " \n " );
}
void emitTraceSlow () {
puts ( " \n //====================== \n\n " );
puts ( "void " + topClassName () + "::trace (" );
if ( optSystemC ()) {
puts ( "SpTraceFile* tfp, int, int) { \n " );
} else {
puts ( "SpTraceVcdCFile* tfp, int, int) { \n " );
}
puts ( "tfp->spTrace()->addCallback ("
"&" + topClassName () + "::traceInit"
+ ", &" + topClassName () + "::traceFull"
+ ", &" + topClassName () + "::traceChg, this); \n " );
puts ( "} \n " );
puts ( "void " + topClassName () + "::traceInit(SpTraceVcd* vcdp, void* userthis, uint32_t code) { \n " );
puts ( "// Callback from vcd->open() \n " );
puts ( topClassName () + "* t=(" + topClassName () + "*)userthis; \n " );
2006-08-30 21:07:55 +00:00
puts ( EmitCBaseVisitor :: symClassVar () + " = t->__VlSymsp; // Setup global symbol table \n " );
2006-08-26 11:35:28 +00:00
puts ( "if (!Verilated::calcUnusedSigs()) vl_fatal(__FILE__,__LINE__,__FILE__, \" Turning on wave traces requires Verilated::traceEverOn(true) call before time 0. \" ); \n " );
2006-08-30 21:07:55 +00:00
puts ( "t->traceInitThis (vlSymsp, vcdp, code); \n " );
2006-08-26 11:35:28 +00:00
puts ( "} \n " );
puts ( "void " + topClassName () + "::traceFull(SpTraceVcd* vcdp, void* userthis, uint32_t code) { \n " );
puts ( "// Callback from vcd->dump() \n " );
puts ( topClassName () + "* t=(" + topClassName () + "*)userthis; \n " );
2006-08-30 21:07:55 +00:00
puts ( EmitCBaseVisitor :: symClassVar () + " = t->__VlSymsp; // Setup global symbol table \n " );
puts ( "t->traceFullThis (vlSymsp, vcdp, code); \n " );
2006-08-26 11:35:28 +00:00
puts ( "} \n " );
puts ( " \n //====================== \n\n " );
}
void emitTraceFast () {
puts ( " \n //====================== \n\n " );
puts ( "void " + topClassName () + "::traceChg(SpTraceVcd* vcdp, void* userthis, uint32_t code) { \n " );
puts ( "// Callback from vcd->dump() \n " );
puts ( topClassName () + "* t=(" + topClassName () + "*)userthis; \n " );
2006-08-30 21:07:55 +00:00
puts ( EmitCBaseVisitor :: symClassVar () + " = t->__VlSymsp; // Setup global symbol table \n " );
puts ( "if (vlSymsp->getClearActivity()) { \n " );
puts ( "t->traceChgThis (vlSymsp, vcdp, code); \n " );
2006-08-26 11:35:28 +00:00
puts ( "} \n " );
puts ( "} \n " );
puts ( " \n //====================== \n\n " );
}
bool emitTraceIsScWide ( AstTraceInc * nodep ) {
AstVarRef * varrefp = nodep -> valuep () -> castVarRef ();
if ( ! varrefp ) return false ;
AstVar * varp = varrefp -> varp ();
return varp -> isSc () && varp -> isScWide ();
}
void emitTraceInitOne ( AstTraceDecl * nodep ) {
if ( nodep -> isWide ()) {
puts ( "vcdp->declArray" );
} else if ( nodep -> isQuad ()) {
puts ( "vcdp->declQuad " );
} else if ( nodep -> msb () || nodep -> lsb ()) {
puts ( "vcdp->declBus " );
} else {
puts ( "vcdp->declBit " );
}
puts ( "(c+" + cvtToStr ( nodep -> code ()));
if ( nodep -> arrayWidth ()) puts ( "+i*" + cvtToStr ( nodep -> widthWords ()));
puts ( ", \" " + nodep -> showname () + " \" " );
if ( nodep -> arrayWidth ()) {
puts ( ",(i+" + cvtToStr ( nodep -> arrayLsb ()) + ")" );
} else {
puts ( ",-1" );
}
if ( nodep -> msb () || nodep -> lsb ()) {
puts ( "," + cvtToStr ( nodep -> msb ()) + "," + cvtToStr ( nodep -> lsb ()));
}
puts ( ");" );
}
void emitTraceChangeOne ( AstTraceInc * nodep , int arrayindex ) {
nodep -> precondsp () -> iterateAndNext ( * this );
string full = ( m_funcp -> funcType () == AstCFuncType :: TRACE_FULL ) ? "full" : "chg" ;
if ( nodep -> isWide () || emitTraceIsScWide ( nodep )) {
puts ( "vcdp->" + full + "Array" );
} else if ( nodep -> isQuad ()) {
puts ( "vcdp->" + full + "Quad " );
} else if ( nodep -> declp () -> msb () || nodep -> declp () -> lsb ()) {
puts ( "vcdp->" + full + "Bus " );
} else {
puts ( "vcdp->" + full + "Bit " );
}
puts ( "(c+" + cvtToStr ( nodep -> declp () -> code ()
+ (( arrayindex < 0 ) ? 0 : ( arrayindex * nodep -> declp () -> widthWords ()))));
puts ( "," );
emitTraceValue ( nodep , arrayindex );
if ( nodep -> declp () -> msb () || nodep -> declp () -> lsb ()) {
puts ( "," + cvtToStr ( nodep -> declp () -> widthMin ()));
}
puts ( "); \n " );
}
void emitTraceValue ( AstTraceInc * nodep , int arrayindex ) {
if ( nodep -> valuep () -> castVarRef ()) {
AstVarRef * varrefp = nodep -> valuep () -> castVarRef ();
AstVar * varp = varrefp -> varp ();
if ( emitTraceIsScWide ( nodep )) puts ( "(uint32_t*)" );
puts ( "(" );
varrefp -> iterate ( * this ); // Put var name out
if ( varp -> arraysp ()) {
if ( arrayindex ==- 2 ) puts ( "[i]" );
else if ( arrayindex ==- 1 ) puts ( "[0]" );
else puts ( "[" + cvtToStr ( arrayindex ) + "]" );
}
if ( varp -> isSc ()) puts ( ".read()" );
if ( emitTraceIsScWide ( nodep )) puts ( ".get_datap()" );
puts ( ")" );
} else {
puts ( "(" );
nodep -> valuep () -> iterate ( * this );
puts ( ")" );
}
}
// VISITORS
virtual void visit ( AstNetlist * nodep , AstNUser * ) {
// Top module only
nodep -> topModulep () -> accept ( * this );
}
virtual void visit ( AstModule * nodep , AstNUser * ) {
nodep -> iterateChildren ( * this );
}
virtual void visit ( AstCFunc * nodep , AstNUser * ) {
if ( nodep -> slow () != m_slow ) return ;
if ( nodep -> funcType () == AstCFuncType :: TRACE_INIT
|| nodep -> funcType () == AstCFuncType :: TRACE_FULL
|| nodep -> funcType () == AstCFuncType :: TRACE_CHANGE ) {
m_funcp = nodep ;
puts ( " \n " );
puts ( nodep -> rtnTypeVoid ()); puts ( " " );
puts ( topClassName () + "::" + nodep -> name ()
+ "(" + cFuncArgs ( nodep ) + ") { \n " );
2006-08-30 22:00:55 +00:00
if ( nodep -> symProlog ()) puts ( EmitCBaseVisitor :: symTopAssign () + " \n " );
2006-08-26 11:35:28 +00:00
puts ( "int c=code; \n " );
puts ( "if (0 && vcdp && c) {} // Prevent unused \n " );
if ( nodep -> funcType () == AstCFuncType :: TRACE_INIT ) {
2006-08-30 22:00:55 +00:00
puts ( "vcdp->module(vlSymsp->name()); // Setup signal names \n " );
2006-08-26 11:35:28 +00:00
} else if ( nodep -> funcType () == AstCFuncType :: TRACE_FULL ) {
} else if ( nodep -> funcType () == AstCFuncType :: TRACE_CHANGE ) {
} else nodep -> v3fatalSrc ( "Bad Case" );
if ( nodep -> initsp ()) puts ( "// Variables \n " );
emitVarList ( nodep -> initsp (), EVL_ALL , "" );
nodep -> initsp () -> iterateAndNext ( * this );
2006-08-29 00:58:48 +00:00
ofp () -> putAlign ( V3OutFile :: AL_AUTO , 4 );
2006-08-26 11:35:28 +00:00
puts ( "// Body \n " );
puts ( "{ \n " );
// Do the statements Note not from this node, but the TRACE_INIT's.
// That saved us from having 3 copies of all of the TRACEs
nodep -> stmtsp () -> iterateAndNext ( * this );
puts ( "} \n " );
if ( nodep -> finalsp ()) puts ( "// Final \n " );
nodep -> finalsp () -> iterateAndNext ( * this );
puts ( "} \n " );
}
m_funcp = NULL ;
}
virtual void visit ( AstTraceDecl * nodep , AstNUser * ) {
if ( nodep -> arrayWidth ()) {
puts ( "{int i; for (i=0; i<" + cvtToStr ( nodep -> arrayWidth ()) + "; i++) { \n " );
emitTraceInitOne ( nodep );
puts ( "}} \n " );
} else {
emitTraceInitOne ( nodep );
puts ( " \n " );
}
}
virtual void visit ( AstTraceInc * nodep , AstNUser * ) {
if ( nodep -> declp () -> arrayWidth ()) {
// It traces faster if we unroll the loop
for ( unsigned i = 0 ; i < nodep -> declp () -> arrayWidth (); i ++ ) {
emitTraceChangeOne ( nodep , i );
}
} else {
emitTraceChangeOne ( nodep , - 1 );
}
}
virtual void visit ( AstCoverDecl * nodep , AstNUser * ) {
}
virtual void visit ( AstCoverInc * nodep , AstNUser * ) {
}
public :
EmitCTrace ( bool slow ) {
m_funcp = NULL ;
m_slow = slow ;
}
virtual ~ EmitCTrace () {}
void main () {
// Put out the file
string filename = ( v3Global . opt . makeDir () + "/" + topClassName ()
+ ( m_slow ? "__Trace__Slow.cpp" : "__Trace.cpp" ));
AstCFile * cfilep = newCFile ( filename , m_slow , true /*source*/ );
cfilep -> support ( true );
V3OutCFile of ( filename );
of . putsHeader ();
m_ofp = & of ;
emitTraceHeader ();
if ( m_slow ) emitTraceSlow ();
else emitTraceFast ();
v3Global . rootp () -> accept ( * this );
m_ofp = NULL ;
}
};
//######################################################################
// EmitC class functions
void V3EmitC :: emitc () {
UINFO ( 2 , __FUNCTION__ << ": " << endl );
// Process each module in turn
for ( AstModule * nodep = v3Global . rootp () -> modulesp (); nodep ; nodep = nodep -> nextp () -> castModule ()) {
if ( v3Global . opt . outputSplit ()) {
{ EmitCImp imp ; imp . main ( nodep , false , true ); }
{ EmitCImp imp ; imp . main ( nodep , true , false ); }
} else {
{ EmitCImp imp ; imp . main ( nodep , true , true ); }
}
}
}
void V3EmitC :: emitcTrace () {
UINFO ( 2 , __FUNCTION__ << ": " << endl );
if ( v3Global . opt . trace ()) {
{ EmitCTrace trace ( true ); trace . main (); }
{ EmitCTrace trace ( false ); trace . main (); }
}
}