41 #include "uhal/log/log.hpp" 43 #include <boost/bind.hpp> 44 #include <boost/spirit/include/qi.hpp> 45 #include <boost/date_time/posix_time/posix_time.hpp> 46 #include <boost/algorithm/string/replace.hpp> 47 #include <boost/filesystem.hpp> 50 #if BOOST_VERSION >= 106000 52 using boost::placeholders::_1;
53 using boost::placeholders::_2;
54 using boost::placeholders::_3;
83 lPlainNode.
optional ( NodeTreeBuilder::mClassAttribute )
84 .forbid ( NodeTreeBuilder::mModuleAttribute )
85 .forbid ( NodeTreeBuilder::mMaskAttribute )
86 .optional ( NodeTreeBuilder::mIdAttribute )
87 .optional ( NodeTreeBuilder::mAddressAttribute )
88 .optional ( NodeTreeBuilder::mParametersAttribute )
89 .optional ( NodeTreeBuilder::mFirmwareInfo )
90 .optional ( NodeTreeBuilder::mPermissionsAttribute )
91 .optional ( NodeTreeBuilder::mModeAttribute )
92 .optional ( NodeTreeBuilder::mSizeAttribute )
93 .optional ( NodeTreeBuilder::mTagsAttribute )
94 .optional ( NodeTreeBuilder::mDescriptionAttribute );
97 lBitMask.
require ( NodeTreeBuilder::mMaskAttribute )
98 .forbid ( NodeTreeBuilder::mClassAttribute )
99 .forbid ( NodeTreeBuilder::mModuleAttribute )
100 .optional ( NodeTreeBuilder::mAddressAttribute )
101 .forbid ( NodeTreeBuilder::mModeAttribute )
102 .forbid ( NodeTreeBuilder::mSizeAttribute )
103 .optional ( NodeTreeBuilder::mParametersAttribute )
104 .optional ( NodeTreeBuilder::mFirmwareInfo )
105 .optional ( NodeTreeBuilder::mPermissionsAttribute )
106 .optional ( NodeTreeBuilder::mTagsAttribute )
107 .optional ( NodeTreeBuilder::mDescriptionAttribute );
110 lModule.
optional ( NodeTreeBuilder::mIdAttribute )
111 .require ( NodeTreeBuilder::mModuleAttribute )
112 .forbid ( NodeTreeBuilder::mMaskAttribute )
113 .optional ( NodeTreeBuilder::mClassAttribute )
114 .forbid ( NodeTreeBuilder::mModeAttribute )
115 .forbid ( NodeTreeBuilder::mSizeAttribute )
116 .forbid ( NodeTreeBuilder::mPermissionsAttribute )
117 .optional ( NodeTreeBuilder::mAddressAttribute )
118 .optional ( NodeTreeBuilder::mParametersAttribute )
119 .optional ( NodeTreeBuilder::mFirmwareInfo )
120 .optional ( NodeTreeBuilder::mTagsAttribute )
121 .optional ( NodeTreeBuilder::mDescriptionAttribute );
127 lPlainNode.
require ( NodeTreeBuilder::mIdAttribute );
128 lBitMask.
require ( NodeTreeBuilder::mIdAttribute );
129 lModule.
require ( NodeTreeBuilder::mIdAttribute );
154 std::vector< std::pair<std::string, std::string> > lAddressFiles;
157 if ( lAddressFiles.size() != 1 )
159 exception::IncorrectAddressTableFileCount lExc;
160 log ( lExc ,
"Exactly one address table file must be specified. The expression " ,
Quote ( aFilenameExpr ) ,
" contains " ,
Integer ( lAddressFiles.size() ) ,
" valid file expressions." );
164 std::vector< const Node* > lNodes;
167 if ( lNodes.size() != 1 )
169 exception::IncorrectAddressTableFileCount lExc;
170 log ( lExc ,
"Exactly one address table file must be specified. The expression " ,
Quote ( lAddressFiles[0].second ) ,
" refers to " ,
Integer ( lNodes.size() ) ,
" valid files." );
174 Node* lNode ( lNodes[0]->clone() );
181 for(boost::unordered_map<std::string, const Node*>::const_iterator it=
mNodes.begin(); it !=
mNodes.end(); it++)
189 void NodeTreeBuilder::CallBack (
const std::string& aProtocol ,
const boost::filesystem::path& aPath , std::vector<uint8_t>& aFile , std::vector< const Node* >& aNodes )
191 std::string lName ( aProtocol + ( aPath.string() ) );
192 boost::unordered_map< std::string , const Node* >::iterator lNodeIt =
mNodes.find ( lName );
194 if ( lNodeIt !=
mNodes.end() )
196 aNodes.push_back ( lNodeIt->second );
200 std::string lExtension ( aPath.extension().string().substr ( 0,4 ) );
201 boost::to_lower ( lExtension );
203 if ( lExtension ==
".xml" )
205 log (
Info() ,
"Reading XML address file " ,
Quote( aPath.c_str() ) );
206 pugi::xml_document lXmlDocument;
207 pugi::xml_parse_result lLoadResult = lXmlDocument.load_buffer_inplace ( & ( aFile[0] ) , aFile.size() );
215 pugi::xml_node lXmlNode = lXmlDocument.child (
"node" );
219 log (
Error() ,
"No XML node called ",
Quote (
"node" ) ,
" in file " , aPath.c_str() );
228 mNodes.insert ( std::make_pair ( lName , lNode ) );
229 aNodes.push_back ( lNode );
232 else if ( lExtension ==
".txt" )
234 log (
Info() ,
"TXT file" );
235 log (
Error() ,
"Parser problems mean that this method has been disabled." );
241 log (
Error() ,
"Extension " ,
Quote ( lExtension ) ,
" not known." );
250 setUid ( aRequireId , aXmlNode , lNode );
262 log (
Debug() , lNode->
mUid ,
" built by " , __PRETTY_FUNCTION__ );
279 setUid ( aRequireId , aXmlNode , lNode );
291 log (
Debug() , lNode->mUid ,
" built by " , __PRETTY_FUNCTION__ );
293 if ( lNode->mClassName.size() )
306 if ( aXmlNode.child (
"node" ) )
308 exception::MaskedNodeCannotHaveChild lExc;
309 log ( lExc ,
"Bit-masked nodes are not allowed to have child nodes" );
314 setUid ( aRequireId , aXmlNode , lNode );
326 log (
Debug() , lNode->
mUid ,
" built by " , __PRETTY_FUNCTION__ );
340 throw exception::NodeMustHaveUID();
352 uint32_t lAddr ( 0 );
361 std::string lClassStr;
369 std::string lParsStr;
373 if ( lParsStr.size() )
376 std::string::const_iterator lBegin ( lParsStr.begin() );
377 std::string::const_iterator lEnd ( lParsStr.end() );
378 boost::unordered_map<std::string, std::string> lPars;
394 if ( lStr.size() && aNode->
mTags.size() )
397 aNode->
mTags += lStr;
400 else if ( lStr.size() && !aNode->
mTags.size() )
436 std::string lPermission;
440 boost::spirit::qi::phrase_parse (
444 boost::spirit::ascii::space,
465 boost::spirit::qi::phrase_parse (
469 boost::spirit::ascii::space,
478 exception::IncrementalNodeRequiresSizeAttribute lExc;
479 log ( lExc ,
"Node " ,
Quote ( aNode->
mUid ) ,
" has type " ,
Quote (
"INCREMENTAL" ) ,
", which requires a " ,
Quote ( NodeTreeBuilder::mSizeAttribute ) ,
" attribute" );
488 log (
Notice() ,
"Node " ,
Quote ( aNode->
mUid ) ,
" has type " ,
Quote (
"NON_INCREMENTAL" ) ,
" but does not have a " ,
Quote ( NodeTreeBuilder::mSizeAttribute ) ,
" attribute. This is not necessarily a problem, but if there is a limit to the size of the read/write operation from this port, then please consider adding this attribute for the sake of safety." );
497 std::string lFwInfoStr;
500 if ( lFwInfoStr.size() )
503 std::string::const_iterator lBegin ( lFwInfoStr.begin() );
504 std::string::const_iterator lEnd ( lFwInfoStr.end() );
507 aNode->
mFirmwareInfo.insert ( make_pair (
"type",lFwInfo.mType ) );
509 if ( lFwInfo.mArguments.size() )
511 aNode->
mFirmwareInfo.insert ( lFwInfo.mArguments.begin() , lFwInfo.mArguments.end() );
524 pugi::xml_node lXmlNode = aXmlNode.child (
"node" );
530 exception::BlockAccessNodeCannotHaveChild lExc;
531 log ( lExc ,
"Block access nodes are not allowed to have child nodes, but the node " ,
Quote ( aNode->
mUid ) ,
" has a child node in the address table" );
537 for ( ; lXmlNode; lXmlNode = lXmlNode.next_sibling (
"node" ) )
542 for ( std::deque< Node* >::iterator lIt = aNode->
mChildren.begin(); lIt != aNode->
mChildren.end(); ++lIt )
544 aNode->
mChildrenMap.insert ( std::make_pair ( ( **lIt ).mUid , *lIt ) );
546 for ( boost::unordered_map< std::string , Node* >::iterator lSubMapIt = ( **lIt ).mChildrenMap.begin() ; lSubMapIt != ( **lIt ).mChildrenMap.end() ; ++lSubMapIt )
548 aNode->
mChildrenMap.insert ( std::make_pair ( ( **lIt ).mUid +
'.'+ ( lSubMapIt->first ) , lSubMapIt->second ) );
565 bool lAllMasked (
true );
567 for ( std::deque< Node* >::iterator lIt = aNode->
mChildren.begin(); lIt != aNode->
mChildren.end(); ++lIt )
595 uint64_t lTopAddr ( ( uint64_t ) ( aNode->
mPartialAddr ) + ( uint64_t ) ( aNode->
mSize-1 ) );
598 if ( lTopAddr >> 32 )
600 exception::ArraySizeExceedsRegisterBound lExc;
625 for ( std::deque< Node* >::iterator lIt = aNode->
mChildren.begin(); lIt != aNode->
mChildren.end(); ++lIt )
627 ( **lIt ).mParent = aNode;
638 std::stringstream lReport;
639 lReport <<
std::hex << std::setfill (
'0' );
640 boost::unordered_map< std::string , Node* >::iterator lIt, lIt2;
641 Node* lNode1, *lNode2;
645 lNode1 = lIt->second;
651 uint32_t lBottom1 ( lNode1->
mAddr );
652 uint32_t lTop1 ( lNode1->
mAddr + ( lNode1->
mSize - 1 ) );
656 lNode2 = lIt2->second;
661 uint32_t lBottom2 ( lNode2->
mAddr );
662 uint32_t lTop2 ( lNode2->
mAddr + ( lNode2->
mSize - 1 ) );
664 if ( ( ( lTop2 >= lBottom1 ) && ( lTop2 <= lTop1 ) ) || ( ( lTop1 >= lBottom2 ) && ( lTop1 <= lTop2 ) ) )
666 lReport <<
"Branch '" << lIt->first
667 <<
"' has address range [0x" << std::setw ( 8 ) << lBottom1 <<
" - 0x" << std::setw ( 8 ) << lTop1
668 <<
"] which overlaps with branch '" << lIt2->first
669 <<
"' which has address range [0x" << std::setw ( 8 ) << lBottom2 <<
" - 0x" << std::setw ( 8 ) << lTop2
670 <<
"]." << std::endl;
671 #ifdef THROW_ON_ADDRESS_SPACE_OVERLAP 672 throw exception::AddressSpaceOverlap();
679 uint32_t lAddr2 ( lNode2->
mAddr );
681 if ( ( lAddr2 >= lBottom1 ) && ( lAddr2 <= lTop1 ) )
683 lReport <<
"Branch '" << lIt->first
684 <<
"' has address range [0x" << std::setw ( 8 ) << lBottom1 <<
" - 0x" << std::setw ( 8 ) << lTop1
685 <<
"] which overlaps with branch '" << lIt2->first
686 <<
"' which has address 0x" << std::setw ( 8 ) << lAddr2
688 #ifdef THROW_ON_ADDRESS_SPACE_OVERLAP 689 throw exception::AddressSpaceOverlap();
697 uint32_t lAddr1 ( lNode1->
mAddr );
701 lNode2 = lIt2->second;
706 uint32_t lBottom2 ( lNode2->
mAddr );
707 uint32_t lTop2 ( lNode2->
mAddr + ( lNode2->
mSize - 1 ) );
709 if ( ( lAddr1 >= lBottom2 ) && ( lAddr1 <= lTop2 ) )
711 lReport <<
"Branch '" << lIt->first
712 <<
"' has address 0x" << std::setw ( 8 ) << lAddr1
713 <<
" which overlaps with branch '" << lIt2->first
714 <<
"' which has address range [0x" << std::setw ( 8 ) << lBottom2 <<
" - 0x" << std::setw ( 8 ) << lTop2
716 #ifdef THROW_ON_ADDRESS_SPACE_OVERLAP 717 throw exception::AddressSpaceOverlap();
724 uint32_t lAddr2 ( lNode2->
mAddr );
726 if ( lAddr1 == lAddr2 )
730 bool lShouldThrow (
true );
732 if ( lNode1->
mMask == 0xFFFFFFFF )
735 for ( std::deque< Node* >::iterator lIt = lNode1->
mChildren.begin() ; lIt != lNode1->
mChildren.end() ; ++lIt )
737 if ( *lIt == lNode2 )
739 lShouldThrow =
false;
745 if ( lShouldThrow && ( lNode2->
mMask == 0xFFFFFFFF ) )
748 for ( std::deque< Node* >::iterator lIt = lNode2->
mChildren.begin() ; lIt != lNode2->
mChildren.end() ; ++lIt )
750 if ( *lIt == lNode1 )
752 lShouldThrow =
false;
760 lReport <<
"Branch '" << lIt->first
761 <<
"' has address 0x" << std::setw ( 8 ) << lAddr1
762 <<
" and mask 0x" << std::setw ( 8 ) << lNode1->
mMask 763 <<
" which overlaps with branch '" << lIt2->first
764 <<
"' which has address 0x" << std::setw ( 8 ) << lAddr2
765 <<
" and mask 0x" << std::setw ( 8 ) << lNode2->
mMask 767 #ifdef THROW_ON_ADDRESS_SPACE_OVERLAP 768 throw exception::AddressSpaceOverlap();
778 if ( lReport.tellp() )
781 std::string lDirName(
"/tmp");
782 if (
char* lUsername = std::getenv(
"USER"))
784 lDirName +=
"/" + std::string(lUsername);
788 boost::filesystem::path lDir ( lDirName );
789 lDir.make_preferred();
792 if ( !boost::filesystem::is_directory ( lDir ) )
794 if ( !boost::filesystem::create_directories ( lDir ) )
796 log (
Error() ,
"Address overlaps observed - attempted and failed to create directory " ,
Quote ( lDirName ) );
801 catch(
const boost::filesystem::filesystem_error& e)
803 log (
Error() ,
"Address overlaps observed - failed to create directory " ,
Quote ( lDirName ) ,
" for report file; caught filesystem_error exception with what returning: ", e.what() );
807 std::string lFilename ( aPath.string() );
808 boost::replace_all ( lFilename ,
"/" ,
"-" );
809 lDir /= (
"OverlapReport" + lFilename +
".txt" );
810 std::ofstream lReportFile ( lDir.c_str() );
812 if ( lReportFile.is_open() )
814 lReportFile <<
"Overlap report for " << aPath <<
"." << std::endl;
815 lReportFile <<
"Written at " << boost::posix_time::microsec_clock::local_time() <<
"." << std::endl;
816 lReportFile << std::endl;
817 lReportFile << lReport.rdbuf();
819 log (
Warning() ,
"Address overlaps observed - report file written at " ,
Quote ( lDir.string() ) );
823 log (
Error() ,
"Address overlaps observed - failed to create report file " ,
Quote ( lDir.string() ) );
A class to build a node tree from an Address table file NOTE! This is a factory method and must be Mu...
void ParseSemicolonDelimitedUriList(const std::string &aSemicolonDelimitedUriList, std::vector< std::pair< std::string, std::string > > &aUriList)
Parse a semicolon delimited list of URIs into a vector of protocol/address pairs. ...
void setTags(const pugi::xml_node &aXmlNode, Node *aNode)
A look-up table that the boost qi parser uses for associating strings ("r","w","rw","wr","read","write","readwrite","writeread") with enumerated permissions types.
boost::unordered_map< std::string, std::string > mFirmwareInfo
parameters to infer the VHDL address decoding
grammars::NodeTreeFirmwareinfoAttributeGrammar mNodeTreeFirmwareInfoAttributeGrammar
Node * bitmaskNodeCreator(const bool &aRequireId, const pugi::xml_node &aXmlNode)
std::string mClassName
Class name used to construct the derived node type.
boost::unordered_map< std::string, const Node *> mNodes
Hash map associating a Node tree with a file name so that we do not need to repeatedly parse the xml ...
static const char * mParametersAttribute
static NodeTreeBuilder * mInstance
The single instance of the class.
Rule< R > & optional(const std::string &aStr)
Add an optional attribute to the rule.
uhal::NodeTreeBuilder::mode_lut mModeLut
An instance of a look-up table that the boost qi parser uses for associating strings with enumerated ...
static const char * mModeAttribute
void setModeAndSize(const pugi::xml_node &aXmlNode, Node *aNode)
static bool NodePtrCompare(Node *aNodeL, Node *aNodeR)
defs::NodePermission mPermission
The read/write access permissions of this node.
static const char * mAddressAttribute
void clearAddressFileCache()
Clears address filename -> Node tree cache. NOT thread safe; for tread-safety, use ConnectionManager ...
permissions_lut()
The actual function that the boost qi parser uses for associating strings with enumerated permissions...
void setUid(const bool &aRequireId, const pugi::xml_node &aXmlNode, Node *aNode)
void CallBack(const std::string &aProtocol, const boost::filesystem::path &aPath, std::vector< uint8_t > &aFile, std::vector< const Node * > &aAddressTable)
Method called once the file specified in the call to getNodeTree( aFilenameExpr ) has been opened...
defs::BlockReadWriteMode mMode
Whether the node represents a single register, a block of registers or a block-read/write port...
std::string mTags
Optional string which the user can specify.
template bool GetXMLattribute< true >(const pugi::xml_node &aNode, const char *aAttrName, std::string &aTarget)
Parser< Node *> mNodeParser
uint32_t mSize
The maximum size available to a block read/write.
uhal::NodeTreeBuilder::permissions_lut mPermissionsLut
An instance of a look-up table that the boost qi parser uses for associating strings with enumerated ...
Node * moduleNodeCreator(const bool &aRequireId, const pugi::xml_node &aXmlNode)
std::string mDescription
Optional string which the user can specify.
const uint32_t NOMASK
define what it means to have no mask
void setFirmwareInfo(const pugi::xml_node &aXmlNode, Node *aNode)
static const char * mModuleAttribute
void addChildren(const pugi::xml_node &aXmlNode, Node *aNode)
Node * plainNodeCreator(const bool &aRequireId, const pugi::xml_node &aXmlNode)
void setClassName(const pugi::xml_node &aXmlNode, Node *aNode)
std::string mModule
The name of the module in which the current node resides.
boost::unordered_map< std::string, std::string > mParameters
Additional parameters of the node.
uint32_t mPartialAddr
The register address with which this node is associated.
static const char * mMaskAttribute
std::deque< boost::filesystem::path > mFileCallStack
uint32_t mMask
The mask to be applied if this node is a sub-field, rather than an entire register.
static const char * mSizeAttribute
void PugiXMLParseResultPrettifier(const pugi::xml_parse_result &aLoadResult, const boost::filesystem::path &aPath, const std::vector< uint8_t > &aFile)
Helper function to make debugging failures when parsing XML files easier.
void setAddr(const pugi::xml_node &aXmlNode, Node *aNode)
static const char * mPermissionsAttribute
A heirarchical node for navigating heirarchical firmwares.
Empty struct which acts as a dummy variable for passing the formatting information around...
_Quote< T > Quote(const T &aT)
Rule< R > & require(const std::string &aStr)
Add a required attribute to the rule.
Struct to store the name and member variables within a node endpoint attribute when parsed by boost s...
Node * convertToClassType(Node *aNode)
void setMask(const pugi::xml_node &aXmlNode, Node *aNode)
void checkForAddressCollisions(Node *aNode, const boost::filesystem::path &aPath)
std::deque< Node *> mChildren
The direct children of the node.
static const char * mFirmwareInfo
static NodeTreeBuilder & getInstance()
Static method to retrieve the single instance of the class.
Rule for matching XML attributes.
static const char * mTagsAttribute
static const char * mDescriptionAttribute
static const char * mIdAttribute
uint32_t mAddr
The register address with which this node is associated.
void setModule(const pugi::xml_node &aXmlNode, Node *aNode)
void calculateHierarchicalAddresses(Node *aNode, const uint32_t &aAddr)
Propagate the addresses down through the hierarchical structure.
void setPermissions(const pugi::xml_node &aXmlNode, Node *aNode)
std::string mUid
The Unique ID of this node.
A look-up table that the boost qi parser uses for associating strings ("single","block","port","incremental","non-incremental","inc","non-inc") with enumerated mode types.
void OpenFile(const std::string &aProtocol, const std::string &aFilenameExpr, const boost::filesystem::path &aParentPath, boost::_bi::bind_t< R, F, L > aBinder)
Given a protocol and either a URL or a linux shell expression, open the file and call the callback fu...
static DerivedNodeFactory & getInstance()
Static method to retrieve the single instance of the class.
Parser< Node *> mTopLevelNodeParser
grammars::NodeTreeParametersGrammar mNodeTreeParametersGrammar
boost::unordered_map< std::string, Node *> mChildrenMap
Helper to assist look-up of a particular child node, given a name.
NodeTreeBuilder()
Default constructor This is private since only a single instance is to be created, using the getInstance method.
static const char * mClassAttribute
void setDescription(const pugi::xml_node &aXmlNode, Node *aNode)
mode_lut()
The actual function that the boost qi parser uses for associating strings with enumerated permissions...
virtual ~NodeTreeBuilder()
Destructor.
template bool GetXMLattribute< false >(const pugi::xml_node &aNode, const char *aAttrName, std::string &aTarget)
void setPars(const pugi::xml_node &aXmlNode, Node *aNode)
_Integer< T, IntFmt<> > Integer(const T &aT)
Forward declare a function which creates an instance of the ultra-lightweight wrapper from an integer...
Node * getNodeTree(const std::string &aFilenameExpr, const boost::filesystem::path &aPath)
Construct a node tree from file whose name is specified.