// -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // DESCRIPTION: Verilator: Emit Verilog from tree // // Code available from: https://verilator.org // //************************************************************************* // // Copyright 2004-2020 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. // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 // //************************************************************************* #include "config_build.h" #include "verilatedos.h" #include "V3Global.h" #include "V3String.h" #include "V3EmitXml.h" #include "V3EmitCBase.h" #include #include //###################################################################### // Emit statements and math operators class EmitXmlFileVisitor final : public AstNVisitor { // NODE STATE // Entire netlist: // AstNode::user1 -> uint64_t, number to connect crossrefs // MEMBERS V3OutFile* m_ofp; uint64_t m_id = 0; // 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 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, V3OutFormatter::LA_XML)); putsNoTracking("\""); } // XML methods void outputId(AstNode* nodep) { if (!nodep->user1()) { nodep->user1(++m_id); } puts("\"" + cvtToStr(nodep->user1()) + "\""); } void outputTag(AstNode* nodep, const string& tagin) { string tag = tagin; if (tag == "") tag = VString::downcase(nodep->typeName()); puts("<" + tag + " " + nodep->fileline()->xml()); puts(" " + nodep->fileline()->xmlDetailedLocation()); 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, const string& tagin) { string tag = tagin; if (tag == "") tag = VString::downcase(nodep->typeName()); if (nodep->op1p() || nodep->op2p() || nodep->op3p() || nodep->op4p()) { puts(">\n"); iterateChildren(nodep); puts("\n"); } else { puts("/>\n"); } } // VISITORS virtual void visit(AstAssignW* nodep) override { outputTag(nodep, "contassign"); // IEEE: vpiContAssign outputChildrenEnd(nodep, "contassign"); } virtual void visit(AstCell* nodep) override { 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) override { puts("\n"); iterateChildren(nodep); puts("\n"); } virtual void visit(AstNodeModule* nodep) override { 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 if (nodep->modPublic()) puts(" public=\"true\""); outputChildrenEnd(nodep, ""); } virtual void visit(AstVar* nodep) override { 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()); // Attributes if (nodep->attrClocker() == VVarAttrClocker::CLOCKER_YES) { puts(" clocker=\"true\""); } else if (nodep->attrClocker() == VVarAttrClocker::CLOCKER_NO) { puts(" clocker=\"false\""); } if (nodep->attrClockEn()) puts(" clock_enable=\"true\""); if (nodep->attrIsolateAssign()) puts(" isolate_assignments=\"true\""); if (nodep->isSigPublic()) puts(" public=\"true\""); if (nodep->isSigUserRdPublic()) puts(" public_flat_rd=\"true\""); if (nodep->isSigUserRWPublic()) puts(" public_flat_rw=\"true\""); if (nodep->isGParam()) { puts(" param=\"true\""); } else if (nodep->isParam()) { puts(" localparam=\"true\""); } if (nodep->attrScBv()) puts(" sc_bv=\"true\""); if (nodep->attrSFormat()) puts(" sformat=\"true\""); outputChildrenEnd(nodep, ""); } virtual void visit(AstPin* nodep) override { // 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) override { outputTag(nodep, ""); puts(" edgeType=\"" + cvtToStr(nodep->edgeType().ascii()) + "\""); // IEEE vpiTopModule outputChildrenEnd(nodep, ""); } virtual void visit(AstModportVarRef* nodep) override { // Dump direction for Modport references string kw = nodep->direction().xmlKwd(); outputTag(nodep, ""); puts(" direction="); putsQuoted(kw); outputChildrenEnd(nodep, ""); } virtual void visit(AstVarXRef* nodep) override { outputTag(nodep, ""); puts(" dotted="); putsQuoted(nodep->dotted()); outputChildrenEnd(nodep, ""); } // Data types virtual void visit(AstBasicDType* nodep) override { outputTag(nodep, "basicdtype"); if (nodep->isRanged()) { puts(" left=\"" + cvtToStr(nodep->left()) + "\""); puts(" right=\"" + cvtToStr(nodep->right()) + "\""); } puts("/>\n"); } virtual void visit(AstIfaceRefDType* nodep) override { string mpn; outputTag(nodep, ""); if (nodep->isModport()) mpn = nodep->modportName(); puts(" modportname="); putsQuoted(mpn); outputChildrenEnd(nodep, ""); } virtual void visit(AstDisplay* nodep) override { outputTag(nodep, ""); puts(" displaytype="); putsQuoted(nodep->verilogKwd()); outputChildrenEnd(nodep, ""); } virtual void visit(AstElabDisplay* nodep) override { outputTag(nodep, ""); puts(" displaytype="); putsQuoted(nodep->verilogKwd()); outputChildrenEnd(nodep, ""); } virtual void visit(AstExtend* nodep) override { outputTag(nodep, ""); puts(" width="); putsQuoted(cvtToStr(nodep->width())); puts(" widthminv="); putsQuoted(cvtToStr(nodep->lhsp()->widthMinV())); outputChildrenEnd(nodep, ""); } virtual void visit(AstExtendS* nodep) override { outputTag(nodep, ""); puts(" width="); putsQuoted(cvtToStr(nodep->width())); puts(" widthminv="); putsQuoted(cvtToStr(nodep->lhsp()->widthMinV())); outputChildrenEnd(nodep, ""); } // Default virtual void visit(AstNode* nodep) override { outputTag(nodep, ""); outputChildrenEnd(nodep, ""); } public: EmitXmlFileVisitor(AstNode* nodep, V3OutFile* ofp) : m_ofp{ofp} { iterate(nodep); } virtual ~EmitXmlFileVisitor() override = default; }; //###################################################################### // List of module files xml visitor class ModuleFilesXmlVisitor final : public AstNVisitor { private: // MEMBERS std::ostream& m_os; std::set m_modulesCovered; std::deque m_nodeModules; // METHODS VL_DEBUG_FUNC; // Declare debug() // VISITORS virtual void visit(AstNetlist* nodep) override { // Children are iterated backwards to ensure correct compilation order iterateChildrenBackwards(nodep); } virtual void visit(AstNodeModule* nodep) override { // 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*) override { // All modules are present at root so no need to iterate on children } public: // CONSTRUCTORS ModuleFilesXmlVisitor(AstNetlist* nodep, std::ostream& os) : m_os(os) { // Need () or GCC 4.8 false warning // Operate on whole netlist nodep->accept(*this); // Xml output m_os << "\n"; for (const FileLine* ifp : m_nodeModules) { m_os << "filenameLetters() << "\" filename=\"" << ifp->filename() << "\" language=\"" << ifp->language().ascii() << "\"/>\n"; } m_os << "\n"; } virtual ~ModuleFilesXmlVisitor() override = default; }; //###################################################################### // Hierarchy of Cells visitor class HierCellsXmlVisitor final : public AstNVisitor { private: // MEMBERS std::ostream& m_os; std::string m_hier; bool m_hasChildren = false; // METHODS VL_DEBUG_FUNC; // Declare debug() // VISITORS virtual void visit(AstNodeModule* nodep) override { if (nodep->level() >= 0 && nodep->level() <= 2) { // ==2 because we don't add wrapper when in XML mode m_os << "\n"; m_os << "fileline()->xml() << " " << nodep->fileline()->xmlDetailedLocation() << " name=\"" << nodep->name() << "\"" << " submodname=\"" << nodep->name() << "\"" << " hier=\"" << nodep->name() << "\""; m_hier = nodep->name() + "."; m_hasChildren = false; iterateChildren(nodep); if (m_hasChildren) { m_os << "\n"; } else { m_os << "/>\n"; } m_os << "\n"; } } virtual void visit(AstCell* nodep) override { if (nodep->modp()->dead()) { return; } if (!m_hasChildren) m_os << ">\n"; m_os << "fileline()->xml() << " " << nodep->fileline()->xmlDetailedLocation() << " 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 << "\n"; } else { m_os << "/>\n"; } m_hier = hier; m_hasChildren = true; } //----- virtual void visit(AstNode* nodep) override { iterateChildren(nodep); } public: // CONSTRUCTORS HierCellsXmlVisitor(AstNetlist* nodep, std::ostream& os) : m_os(os) { // Need () or GCC 4.8 false warning // Operate on whole netlist nodep->accept(*this); } virtual ~HierCellsXmlVisitor() override = default; }; //###################################################################### // EmitXml class functions void V3EmitXml::emitxml() { UINFO(2, __FUNCTION__ << ": " << endl); // All-in-one file string filename = (v3Global.opt.xmlOutput().empty() ? v3Global.opt.makeDir() + "/" + v3Global.opt.prefix() + ".xml" : v3Global.opt.xmlOutput()); V3OutXmlFile of(filename); of.putsHeader(); of.puts("\n"); of.puts("\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("\n"); }