354 lines
12 KiB
C++
354 lines
12 KiB
C++
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||
//*************************************************************************
|
||
// DESCRIPTION: Verilator: Emit Verilog from tree
|
||
//
|
||
// Code available from: http://www.veripool.org/verilator
|
||
//
|
||
//*************************************************************************
|
||
//
|
||
// Copyright 2004-2019 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.
|
||
//
|
||
// 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_build.h"
|
||
#include "verilatedos.h"
|
||
|
||
#include "V3Global.h"
|
||
#include "V3String.h"
|
||
#include "V3EmitXml.h"
|
||
#include "V3EmitCBase.h"
|
||
|
||
#include <algorithm>
|
||
#include <cmath>
|
||
#include <cstdarg>
|
||
#include <map>
|
||
#include <vector>
|
||
|
||
//######################################################################
|
||
// Emit statements and math operators
|
||
|
||
class EmitXmlFileVisitor : public AstNVisitor {
|
||
// NODE STATE
|
||
//Entire netlist:
|
||
// AstNode::user1 -> uint64_t, number to connect crossrefs
|
||
|
||
// MEMBERS
|
||
V3OutFile* m_ofp;
|
||
uint64_t m_id;
|
||
|
||
// METHODS
|
||
VL_DEBUG_FUNC; // Declare debug()
|
||
|
||
// Outfile methods
|
||
V3OutFile* ofp() const { return m_ofp; }
|
||
virtual void puts(const string& str) { ofp()->puts(str); }
|
||
virtual void putbs(const string& str) { ofp()->putbs(str); }
|
||
virtual void putfs(AstNode*, const string& str) { putbs(str); }
|
||
virtual void putqs(AstNode*, const string& str) { putbs(str); }
|
||
virtual void putsNoTracking(const string& str) { ofp()->putsNoTracking(str); }
|
||
virtual void putsQuoted(const string& str) {
|
||
// Quote \ and " for use inside C programs
|
||
// Don't use to quote a filename for #include - #include doesn't \ escape.
|
||
// Duplicate in V3File - here so we can print to string
|
||
putsNoTracking("\"");
|
||
putsNoTracking(V3OutFormatter::quoteNameControls(str));
|
||
putsNoTracking("\"");
|
||
}
|
||
|
||
// XML methods
|
||
void outputId(AstNode* nodep) {
|
||
if (!nodep->user1()) { nodep->user1(++m_id); }
|
||
puts("\""+cvtToStr(nodep->user1())+"\"");
|
||
}
|
||
void outputTag(AstNode* nodep, string tag) {
|
||
if (tag=="") tag = VString::downcase(nodep->typeName());
|
||
puts("<"+tag+" "+nodep->fileline()->xml());
|
||
if (VN_IS(nodep, NodeDType)) { puts(" id="); outputId(nodep); }
|
||
if (nodep->name()!="") { puts(" name="); putsQuoted(nodep->prettyName()); }
|
||
if (nodep->tag()!="") { puts(" tag="); putsQuoted(nodep->tag()); }
|
||
if (AstNodeDType* dtp = VN_CAST(nodep, NodeDType)) {
|
||
if (dtp->subDTypep()) {
|
||
puts(" sub_dtype_id="); outputId(dtp->subDTypep()->skipRefp());
|
||
}
|
||
} else {
|
||
if (nodep->dtypep()) { puts(" dtype_id="); outputId(nodep->dtypep()->skipRefp()); }
|
||
}
|
||
}
|
||
void outputChildrenEnd(AstNode* nodep, string tag) {
|
||
if (tag=="") tag = VString::downcase(nodep->typeName());
|
||
if (nodep->op1p() || nodep->op2p() || nodep->op3p() || nodep->op4p()) {
|
||
puts(">\n");
|
||
iterateChildren(nodep);
|
||
puts("</"+tag+">\n");
|
||
} else {
|
||
puts("/>\n");
|
||
}
|
||
}
|
||
|
||
// VISITORS
|
||
virtual void visit(AstAssignW* nodep) {
|
||
outputTag(nodep, "contassign"); // IEEE: vpiContAssign
|
||
outputChildrenEnd(nodep, "contassign");
|
||
}
|
||
virtual void visit(AstCell* nodep) {
|
||
outputTag(nodep, "instance"); // IEEE: vpiInstance
|
||
puts(" defName="); putsQuoted(nodep->modName()); // IEEE vpiDefName
|
||
puts(" origName="); putsQuoted(nodep->origName());
|
||
outputChildrenEnd(nodep, "instance");
|
||
}
|
||
virtual void visit(AstNetlist* nodep) {
|
||
puts("<netlist>\n");
|
||
iterateChildren(nodep);
|
||
puts("</netlist>\n");
|
||
}
|
||
virtual void visit(AstNodeModule* nodep) {
|
||
outputTag(nodep, "");
|
||
puts(" origName="); putsQuoted(nodep->origName());
|
||
if (nodep->level()==1 || nodep->level()==2) // ==2 because we don't add wrapper when in XML mode
|
||
puts(" topModule=\"1\""); // IEEE vpiTopModule
|
||
outputChildrenEnd(nodep, "");
|
||
}
|
||
virtual void visit(AstVar* nodep) {
|
||
AstVarType typ = nodep->varType();
|
||
string kw = nodep->verilogKwd();
|
||
string vt = nodep->dtypep()->name();
|
||
outputTag(nodep, "");
|
||
if (nodep->isIO()) {
|
||
puts(" dir="); putsQuoted(kw);
|
||
puts(" vartype="); putsQuoted(!vt.empty()
|
||
? vt : typ == AstVarType::PORT ? "port" : "unknown");
|
||
} else {
|
||
puts(" vartype="); putsQuoted(!vt.empty() ? vt : kw);
|
||
}
|
||
puts(" origName="); putsQuoted(nodep->origName());
|
||
outputChildrenEnd(nodep, "");
|
||
}
|
||
virtual void visit(AstPin* nodep) {
|
||
// What we call a pin in verilator is a port in the IEEE spec.
|
||
outputTag(nodep, "port"); // IEEE: vpiPort
|
||
if (nodep->modVarp()->isIO()) {
|
||
puts(" direction=\""+nodep->modVarp()->direction().xmlKwd()+"\"");
|
||
}
|
||
puts(" portIndex=\""+cvtToStr(nodep->pinNum())+"\""); // IEEE: vpiPortIndex
|
||
// Children includes vpiHighConn and vpiLowConn; we don't support port bits (yet?)
|
||
outputChildrenEnd(nodep, "port");
|
||
}
|
||
virtual void visit(AstSenItem* nodep) {
|
||
outputTag(nodep, "");
|
||
puts(" edgeType=\""+cvtToStr(nodep->edgeType().ascii())+"\""); // IEEE vpiTopModule
|
||
outputChildrenEnd(nodep, "");
|
||
}
|
||
virtual void visit(AstModportVarRef* nodep) {
|
||
// Dump direction for Modport references
|
||
string kw = nodep->direction().xmlKwd();
|
||
outputTag(nodep, "");
|
||
puts(" direction="); putsQuoted(kw);
|
||
outputChildrenEnd(nodep, "");
|
||
}
|
||
virtual void visit(AstVarXRef* nodep) {
|
||
outputTag(nodep, "");
|
||
puts(" dotted="); putsQuoted(nodep->dotted());
|
||
outputChildrenEnd(nodep, "");
|
||
}
|
||
|
||
// Data types
|
||
virtual void visit(AstBasicDType* nodep) {
|
||
outputTag(nodep, "basicdtype ");
|
||
if (nodep->isRanged()) {
|
||
puts(" left=\""+cvtToStr(nodep->left())+"\"");
|
||
puts(" right=\""+cvtToStr(nodep->right())+"\"");
|
||
}
|
||
puts("/>\n");
|
||
}
|
||
virtual void visit(AstIfaceRefDType* nodep) {
|
||
string mpn;
|
||
outputTag(nodep, "");
|
||
if (nodep->isModport()) mpn = nodep->modportName();
|
||
puts(" modportname="); putsQuoted(mpn);
|
||
outputChildrenEnd(nodep, "");
|
||
}
|
||
virtual void visit(AstDisplay* nodep) {
|
||
outputTag(nodep, "");
|
||
puts(" displaytype="); putsQuoted(nodep->verilogKwd());
|
||
outputChildrenEnd(nodep, "");
|
||
}
|
||
virtual void visit(AstExtend* nodep) {
|
||
outputTag(nodep, "");
|
||
puts(" width="); putsQuoted(cvtToStr(nodep->width()));
|
||
puts(" widthminv="); putsQuoted(cvtToStr(nodep->lhsp()->widthMinV()));
|
||
outputChildrenEnd(nodep, "");
|
||
}
|
||
virtual void visit(AstExtendS* nodep) {
|
||
outputTag(nodep, "");
|
||
puts(" width="); putsQuoted(cvtToStr(nodep->width()));
|
||
puts(" widthminv="); putsQuoted(cvtToStr(nodep->lhsp()->widthMinV()));
|
||
outputChildrenEnd(nodep, "");
|
||
}
|
||
|
||
// Default
|
||
virtual void visit(AstNode* nodep) {
|
||
outputTag(nodep, "");
|
||
outputChildrenEnd(nodep, "");
|
||
}
|
||
public:
|
||
EmitXmlFileVisitor(AstNode* nodep, V3OutFile* ofp) {
|
||
m_ofp = ofp;
|
||
m_id = 0;
|
||
iterate(nodep);
|
||
}
|
||
virtual ~EmitXmlFileVisitor() {}
|
||
};
|
||
|
||
//######################################################################
|
||
// List of module files xml visitor
|
||
|
||
class ModuleFilesXmlVisitor : public AstNVisitor {
|
||
private:
|
||
// MEMBERS
|
||
std::ostream& m_os;
|
||
std::set<std::string> m_modulesCovered;
|
||
std::deque<FileLine*> m_nodeModules;
|
||
|
||
// METHODS
|
||
VL_DEBUG_FUNC; // Declare debug()
|
||
|
||
// VISITORS
|
||
virtual void visit(AstNetlist* nodep) {
|
||
// Children are iterated backwards to ensure correct compilation order
|
||
iterateChildrenBackwards(nodep);
|
||
}
|
||
virtual void visit(AstNodeModule* nodep) {
|
||
// Only list modules and interfaces
|
||
// Assumes modules and interfaces list is already sorted level wise
|
||
if (!nodep->dead()
|
||
&& (VN_IS(nodep, Module) || VN_IS(nodep, Iface))
|
||
&& m_modulesCovered.insert(nodep->fileline()->filename()).second) {
|
||
m_nodeModules.push_front(nodep->fileline());
|
||
}
|
||
}
|
||
//-----
|
||
virtual void visit(AstNode* nodep) {
|
||
// All modules are present at root so no need to iterate on children
|
||
}
|
||
|
||
public:
|
||
// CONSTRUCTORS
|
||
ModuleFilesXmlVisitor(AstNetlist* nodep, std::ostream& os)
|
||
: m_os(os), m_modulesCovered(), m_nodeModules() {
|
||
// Operate on whole netlist
|
||
nodep->accept(*this);
|
||
// Xml output
|
||
m_os<<"<module_files>\n";
|
||
for (std::deque<FileLine*>::iterator it = m_nodeModules.begin();
|
||
it != m_nodeModules.end(); it++) {
|
||
m_os<<"<file id=\""<<(*it)->filenameLetters()
|
||
<<"\" filename=\""<<(*it)->filename()
|
||
<<"\" language=\""<<(*it)->language().ascii()<<"\"/>\n";
|
||
}
|
||
m_os<<"</module_files>\n";
|
||
}
|
||
virtual ~ModuleFilesXmlVisitor() {}
|
||
};
|
||
|
||
//######################################################################
|
||
// Hierarchy of Cells visitor
|
||
|
||
class HierCellsXmlVisitor : public AstNVisitor {
|
||
private:
|
||
// MEMBERS
|
||
std::ostream& m_os;
|
||
std::string m_hier;
|
||
bool m_hasChildren;
|
||
|
||
// METHODS
|
||
VL_DEBUG_FUNC; // Declare debug()
|
||
|
||
// VISITORS
|
||
virtual void visit(AstNodeModule* nodep) {
|
||
if (nodep->level() >= 0
|
||
&& nodep->level() <=2 ) { // ==2 because we don't add wrapper when in XML mode
|
||
m_os<<"<cells>\n";
|
||
m_os<<"<cell "<<nodep->fileline()->xml()
|
||
<<" name=\""<<nodep->name()<<"\""
|
||
<<" submodname=\""<<nodep->name()<<"\""
|
||
<<" hier=\""<<nodep->name()<<"\"";
|
||
m_hier = nodep->name() + ".";
|
||
m_hasChildren = false;
|
||
iterateChildren(nodep);
|
||
if (m_hasChildren) {
|
||
m_os<<"</cell>\n";
|
||
} else {
|
||
m_os<<"/>\n";
|
||
}
|
||
m_os<<"</cells>\n";
|
||
}
|
||
}
|
||
virtual void visit(AstCell* nodep) {
|
||
if (nodep->modp()->dead()) {
|
||
return;
|
||
}
|
||
if (!m_hasChildren) m_os<<">\n";
|
||
m_os<<"<cell "<<nodep->fileline()->xml()
|
||
<<" name=\""<<nodep->name()<<"\""
|
||
<<" submodname=\""<<nodep->modName()<<"\""
|
||
<<" hier=\""<<m_hier+nodep->name()<<"\"";
|
||
std::string hier = m_hier;
|
||
m_hier += nodep->name() + ".";
|
||
m_hasChildren = false;
|
||
iterateChildren(nodep->modp());
|
||
if (m_hasChildren) {
|
||
m_os<<"</cell>\n";
|
||
} else {
|
||
m_os<<"/>\n";
|
||
}
|
||
m_hier = hier;
|
||
m_hasChildren = true;
|
||
}
|
||
//-----
|
||
virtual void visit(AstNode* nodep) {
|
||
iterateChildren(nodep);
|
||
}
|
||
|
||
public:
|
||
// CONSTRUCTORS
|
||
HierCellsXmlVisitor(AstNetlist* nodep, std::ostream& os)
|
||
: m_os(os), m_hier(""), m_hasChildren(false) {
|
||
// Operate on whole netlist
|
||
nodep->accept(*this);
|
||
}
|
||
virtual ~HierCellsXmlVisitor() {}
|
||
};
|
||
|
||
//######################################################################
|
||
// EmitXml class functions
|
||
|
||
void V3EmitXml::emitxml() {
|
||
UINFO(2,__FUNCTION__<<": "<<endl);
|
||
// All-in-one file
|
||
V3OutXmlFile of (v3Global.opt.makeDir()+"/"+v3Global.opt.prefix()+".xml");
|
||
of.putsHeader();
|
||
of.puts("<!-- DESCR" "IPTION: Verilator output: XML representation of netlist -->\n");
|
||
of.puts("<verilator_xml>\n");
|
||
{
|
||
std::stringstream sstr;
|
||
FileLine::fileNameNumMapDumpXml(sstr);
|
||
of.puts(sstr.str());
|
||
}
|
||
{
|
||
std::stringstream sstr;
|
||
ModuleFilesXmlVisitor moduleFilesVisitor (v3Global.rootp(), sstr);
|
||
HierCellsXmlVisitor cellsVisitor (v3Global.rootp(), sstr);
|
||
of.puts(sstr.str());
|
||
}
|
||
EmitXmlFileVisitor visitor (v3Global.rootp(), &of);
|
||
of.puts("</verilator_xml>\n");
|
||
}
|