μHAL (v2.8.17)
Part of the IPbus software repository
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
NodeTreeBuilder.cpp
Go to the documentation of this file.
1/*
2---------------------------------------------------------------------------
3
4 This file is part of uHAL.
5
6 uHAL is a hardware access library and programming framework
7 originally developed for upgrades of the Level-1 trigger of the CMS
8 experiment at CERN.
9
10 uHAL is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 uHAL is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with uHAL. If not, see <http://www.gnu.org/licenses/>.
22
23
24 Andrew Rose, Imperial College, London
25 email: awr01 <AT> imperial.ac.uk
26
27 Marc Magrans de Abril, CERN
28 email: marc.magrans.de.abril <AT> cern.ch
29
30---------------------------------------------------------------------------
31*/
32
34
35
36#include <chrono>
37#include <functional>
38
39#include <boost/spirit/include/qi.hpp>
40#include <boost/date_time/posix_time/posix_time.hpp>
41#include <boost/algorithm/string/replace.hpp>
42#include <boost/filesystem.hpp>
43
46#include "uhal/log/log.hpp"
49
50
51// Resolve std bind placeholders (_1, _2, ...)
52namespace arg = std::placeholders;
53
54
55namespace uhal
56{
57
58 const std::string NodeTreeBuilder::mIdAttribute = "id";
59 const std::string NodeTreeBuilder::mAddressAttribute = "address";
60 const std::string NodeTreeBuilder::mParametersAttribute = "parameters";
61 const std::string NodeTreeBuilder::mTagsAttribute = "tags";
62 const std::string NodeTreeBuilder::mDescriptionAttribute = "description";
63 const std::string NodeTreeBuilder::mPermissionsAttribute = "permission";
64 const std::string NodeTreeBuilder::mMaskAttribute = "mask";
65 const std::string NodeTreeBuilder::mModeAttribute = "mode";
66 const std::string NodeTreeBuilder::mSizeAttribute = "size";
67 const std::string NodeTreeBuilder::mClassAttribute = "class";
68 const std::string NodeTreeBuilder::mModuleAttribute = "module";
69 const std::string NodeTreeBuilder::mFirmwareInfo = "fwinfo";
70
71
72 std::shared_ptr<NodeTreeBuilder> NodeTreeBuilder::mInstance;
73
74
76 {
77 //------------------------------------------------------------------------------------------------------------------------
78 Rule<Node*> lPlainNode;
79 lPlainNode.optional ( NodeTreeBuilder::mClassAttribute ) //.forbid ( NodeTreeBuilder::mClassAttribute ) //see https://svnweb.cern.ch/trac/cactus/ticket/452
91 //------------------------------------------------------------------------------------------------------------------------
92 Rule<Node*> lBitMask;
96 .optional ( NodeTreeBuilder::mAddressAttribute ) // .forbid ( NodeTreeBuilder::mAddressAttribute ) //see https://svnweb.cern.ch/trac/cactus/ticket/92
104 //------------------------------------------------------------------------------------------------------------------------
105 Rule<Node*> lModule;
109 .optional ( NodeTreeBuilder::mClassAttribute ) //.forbid ( NodeTreeBuilder::mClassAttribute ) //see https://svnweb.cern.ch/trac/cactus/ticket/452
118 //------------------------------------------------------------------------------------------------------------------------
119 mTopLevelNodeParser.addRule ( lPlainNode , std::bind ( &NodeTreeBuilder::plainNodeCreator , this , false , arg::_1 ) );
120 mTopLevelNodeParser.addRule ( lBitMask , std::bind ( &NodeTreeBuilder::bitmaskNodeCreator , this , false , arg::_1 ) );
121 mTopLevelNodeParser.addRule ( lModule , std::bind ( &NodeTreeBuilder::moduleNodeCreator , this , false , arg::_1 ) );
122 //------------------------------------------------------------------------------------------------------------------------
126 //------------------------------------------------------------------------------------------------------------------------
127 mNodeParser.addRule ( lPlainNode , std::bind ( &NodeTreeBuilder::plainNodeCreator , this , true , arg::_1 ) );
128 mNodeParser.addRule ( lBitMask , std::bind ( &NodeTreeBuilder::bitmaskNodeCreator , this , true , arg::_1 ) );
129 mNodeParser.addRule ( lModule , std::bind ( &NodeTreeBuilder::moduleNodeCreator , this , true , arg::_1 ) );
130 //------------------------------------------------------------------------------------------------------------------------
131 }
132
133
135 {
137 }
138
139
141 {
142 if ( ! mInstance )
143 {
144 mInstance.reset(new NodeTreeBuilder());
145 }
146
147 return *mInstance;
148 }
149
150
151 Node* NodeTreeBuilder::getNodeTree ( const std::string& aFilenameExpr , const boost::filesystem::path& aPath )
152 {
153 std::vector< std::pair<std::string, std::string> > lAddressFiles;
154 uhal::utilities::ParseSemicolonDelimitedUriList ( aFilenameExpr , lAddressFiles );
155
156 if ( lAddressFiles.size() != 1 )
157 {
158 exception::IncorrectAddressTableFileCount lExc;
159 log ( lExc , "Exactly one address table file must be specified. The expression " , Quote ( aFilenameExpr ) , " contains " , Integer ( lAddressFiles.size() ) , " valid file expressions." );
160 throw lExc;
161 }
162
163 std::vector< const Node* > lNodes;
164 uhal::utilities::OpenFile ( lAddressFiles[0].first , lAddressFiles[0].second , aPath.parent_path() , std::bind ( &NodeTreeBuilder::CallBack, std::ref ( *this ) , arg::_1 , arg::_2 , arg::_3 , std::ref ( lNodes ) ) );
165
166 if ( lNodes.size() != 1 )
167 {
168 exception::IncorrectAddressTableFileCount lExc;
169 log ( lExc , "Exactly one address table file must be specified. The expression " , Quote ( lAddressFiles[0].second ) , " refers to " , Integer ( lNodes.size() ) , " valid files." );
170 throw lExc;
171 }
172
173 Node* lNode ( lNodes[0]->clone() );
174 return lNode;
175 }
176
177
179 {
180 for(const auto& x: mNodes)
181 delete x.second;
182 mNodes.clear();
183 }
184
185
186 Node* NodeTreeBuilder::build(const pugi::xml_node& aNode, const boost::filesystem::path& aAddressFilePath)
187 {
188 mFileCallStack.push_back ( aAddressFilePath );
189 Node* lNode ( mTopLevelNodeParser ( aNode ) );
190 mFileCallStack.pop_back( );
191 calculateHierarchicalAddresses ( lNode , 0x00000000 );
192 checkForAddressCollisions ( lNode , aAddressFilePath ); // Needs further investigation - disabled for now as it causes exceptions with valid tables.
193
194 return lNode;
195 }
196
197
198 void NodeTreeBuilder::CallBack ( const std::string& aProtocol , const boost::filesystem::path& aPath , std::vector<uint8_t>& aFile , std::vector< const Node* >& aNodes )
199 {
200 std::string lName ( aProtocol + ( aPath.string() ) );
201 std::unordered_map< std::string , const Node* >::iterator lNodeIt = mNodes.find ( lName );
202
203 if ( lNodeIt != mNodes.end() )
204 {
205 aNodes.push_back ( lNodeIt->second );
206 return;
207 }
208
209 std::string lExtension ( aPath.extension().string().substr ( 0,4 ) );
210 boost::to_lower ( lExtension ); //just in case someone decides to use capitals in their file extensions.
211
212 if ( lExtension == ".xml" )
213 {
214 log ( Info() , "Reading XML address file " , Quote( aPath.c_str() ) );
215 pugi::xml_document lXmlDocument;
216 pugi::xml_parse_result lLoadResult = lXmlDocument.load_buffer_inplace ( & ( aFile[0] ) , aFile.size() );
217
218 if ( !lLoadResult )
219 {
220 uhal::utilities::PugiXMLParseResultPrettifier ( lLoadResult , aPath , aFile );
221 return;
222 }
223
224 pugi::xml_node lXmlNode = lXmlDocument.child ( "node" );
225
226 if ( !lXmlNode )
227 {
228 log ( Error() , "No XML node called ", Quote ( "node" ) , " in file " , aPath.c_str() );
229 return;
230 }
231
232 Node* lNode ( build ( lXmlNode , aPath ) );
233 mNodes.insert ( std::make_pair ( lName , lNode ) );
234 aNodes.push_back ( lNode );
235 return;
236 }
237 else if ( lExtension == ".txt" )
238 {
239 log ( Info() , "TXT file" );
240 log ( Error() , "Parser problems mean that this method has been disabled." );
241 return;
242 }
243 else
244 {
245 log ( Error() , "Extension " , Quote ( lExtension ) , " not known." );
246 return;
247 }
248 }
249
250
251 Node* NodeTreeBuilder::plainNodeCreator ( const bool& aRequireId , const pugi::xml_node& aXmlNode )
252 {
253 Node* lNode ( new Node() );
254 setUid ( aRequireId , aXmlNode , lNode );
255 setAddr ( aXmlNode , lNode );
256 setPars ( aXmlNode , lNode );
257 setFirmwareInfo ( aXmlNode , lNode );
258 setClassName ( aXmlNode , lNode );
259 setTags ( aXmlNode , lNode );
260 setDescription ( aXmlNode , lNode );
261 setModule ( aXmlNode , lNode );
262 setPermissions ( aXmlNode , lNode );
263 //setMask( aXmlNode , lNode );
264 setModeAndSize ( aXmlNode , lNode );
265 addChildren ( aXmlNode , lNode );
266 log ( Debug() , lNode->mUid , " built by " , __PRETTY_FUNCTION__ );
267
268 if ( lNode->mClassName.size() )
269 {
271 }
272 else
273 {
274 return lNode;
275 }
276 }
277
278 Node* NodeTreeBuilder::moduleNodeCreator ( const bool& aRequireId , const pugi::xml_node& aXmlNode )
279 {
280 std::string lModule;
282 Node* lNode ( getNodeTree ( lModule , mFileCallStack.back( ) ) );
283 setUid ( aRequireId , aXmlNode , lNode );
284 setAddr ( aXmlNode , lNode );
285 setClassName ( aXmlNode , lNode );
286 setPars ( aXmlNode , lNode );
287 setFirmwareInfo ( aXmlNode , lNode );
288 setTags ( aXmlNode , lNode );
289 setDescription ( aXmlNode , lNode );
290 setModule ( aXmlNode , lNode );
291 //setPermissions( aXmlNode , lNode );
292 //setMask( aXmlNode , lNode );
293 //setModeAndSize( aXmlNode , lNode );
294 //addChildren( aXmlNode , lNode );
295 log ( Debug() , lNode->mUid , " built by " , __PRETTY_FUNCTION__ );
296
297 if ( lNode->mClassName.size() )
298 {
300 }
301 else
302 {
303 return lNode;
304 }
305 }
306
307
308 Node* NodeTreeBuilder::bitmaskNodeCreator ( const bool& aRequireId , const pugi::xml_node& aXmlNode )
309 {
310 if ( aXmlNode.child ( "node" ) )
311 {
312 exception::MaskedNodeCannotHaveChild lExc;
313 log ( lExc , "Bit-masked nodes are not allowed to have child nodes" );
314 throw lExc;
315 }
316
317 Node* lNode ( new Node() );
318 setUid ( aRequireId , aXmlNode , lNode );
319 setAddr ( aXmlNode , lNode ); //was commented out, see https://svnweb.cern.ch/trac/cactus/ticket/92
320 setClassName ( aXmlNode , lNode );
321 setPars ( aXmlNode , lNode );
322 setFirmwareInfo ( aXmlNode , lNode );
323 setTags ( aXmlNode , lNode );
324 setDescription ( aXmlNode , lNode );
325 setModule ( aXmlNode , lNode );
326 setPermissions ( aXmlNode , lNode );
327 setMask ( aXmlNode , lNode );
328 //setModeAndSize( aXmlNode , lNode );
329 //addChildren( aXmlNode , lNode );
330 log ( Debug() , lNode->mUid , " built by " , __PRETTY_FUNCTION__ );
331 return lNode;
332 }
333
334
335
336
337 void NodeTreeBuilder::setUid ( const bool& aRequireId , const pugi::xml_node& aXmlNode , Node* aNode )
338 {
339 const bool lHasId = uhal::utilities::GetXMLattribute<false> ( aXmlNode , NodeTreeBuilder::mIdAttribute , aNode->mUid );
340
341 if ( aRequireId and ( not lHasId ) )
342 {
343 //error description is given in the function itself so no more elaboration required
344 throw exception::NodeMustHaveUID("'id' attribute is missing from address table node");
345 }
346
347 if ( lHasId )
348 {
349 if ( aNode->mUid.empty() )
350 throw exception::NodeAttributeIncorrectValue("Invalid node ID specified (empty)");
351 else if ( aNode->mUid.find('.') != std::string::npos )
352 throw exception::NodeAttributeIncorrectValue("Invalid node ID '" + aNode->mUid + "' specified (contains dots)");
353 else if ( ( aNode->mUid.at(0) == ' ' ) or ( aNode->mUid.at(aNode->mUid.size()-1) == ' ' ) )
354 throw exception::NodeAttributeIncorrectValue("Invalid node ID '" + aNode->mUid + "' specified (contains spaces)");
355 }
356 }
357
358 void NodeTreeBuilder::setAddr ( const pugi::xml_node& aXmlNode , Node* aNode )
359 {
360 //Address is an optional attribute for hierarchical addressing
361 uint32_t lAddr ( 0 );
363 aNode->mPartialAddr |= lAddr;
364 }
365
366
367 void NodeTreeBuilder::setClassName ( const pugi::xml_node& aXmlNode , Node* aNode )
368 {
369 //Address is an optional attribute for hierarchical addressing
370 std::string lClassStr;
372
373 aNode->mClassName = lClassStr;
374 }
375
376 void NodeTreeBuilder::setPars ( const pugi::xml_node& aXmlNode , Node* aNode )
377 {
378 std::string lParsStr;
379 //get attribute from xml file as string
381
382 if ( lParsStr.size() )
383 {
384 //parse the string into a NodeTreeParameters object
385 std::string::const_iterator lBegin ( lParsStr.begin() );
386 std::string::const_iterator lEnd ( lParsStr.end() );
387 std::unordered_map<std::string, std::string> lPars;
388 boost::spirit::qi::phrase_parse ( lBegin , lEnd , mNodeTreeParametersGrammar , boost::spirit::ascii::space , lPars );
389 // Update the parameters map
390 // Add to lPars those previously defined (module node)
391 lPars.insert ( aNode->mParameters.begin(), aNode->mParameters.end() );
392 // Swap the containers
393 aNode->mParameters.swap ( lPars );
394 }
395 }
396
397 void NodeTreeBuilder::setTags ( const pugi::xml_node& aXmlNode , Node* aNode )
398 {
399 std::string lStr;
400 //Tags is an optional attribute to allow the user to add a description to a node
402
403 if ( lStr.size() && aNode->mTags.size() )
404 {
405 aNode->mTags += "[";
406 aNode->mTags += lStr;
407 aNode->mTags += "]";
408 }
409 else if ( lStr.size() && !aNode->mTags.size() )
410 {
411 aNode->mTags = lStr;
412 }
413 }
414
415
416 void NodeTreeBuilder::setDescription ( const pugi::xml_node& aXmlNode , Node* aNode )
417 {
418 std::string lStr;
419 //Tags is an optional attribute to allow the user to add a description to a node
421
422 if ( lStr.size() && aNode->mDescription.size() )
423 {
424 aNode->mDescription += "[";
425 aNode->mDescription += lStr;
426 aNode->mDescription += "]";
427 }
428 else if ( lStr.size() && !aNode->mDescription.size() )
429 {
430 aNode->mDescription = lStr;
431 }
432 }
433
435 {
436 if ( mFileCallStack.size() )
437 {
438 aNode->mModule = mFileCallStack.back( ).string();
439 }
440 }
441
442 void NodeTreeBuilder::setPermissions ( const pugi::xml_node& aXmlNode , Node* aNode )
443 {
444 //Permissions is an optional attribute for specifying read/write permissions
445 std::string lPermissionAttr;
446
448 {
449 const defs::NodePermission* const lPermission = mPermissionsLut.find(lPermissionAttr.c_str());
450 if (lPermission == NULL)
451 {
452 throw exception::NodeAttributeIncorrectValue("Permission attribute for node with ID '" + aNode->mUid + "' has incorrect value '" + lPermissionAttr + "'");
453 }
454 else
455 aNode->mPermission = *lPermission;
456 }
457 }
458
459
460 void NodeTreeBuilder::setMask ( const pugi::xml_node& aXmlNode , Node* aNode )
461 {
462 //Tags is an optional attribute to allow the user to add a description to a node
464 }
465
466
467 void NodeTreeBuilder::setModeAndSize ( const pugi::xml_node& aXmlNode , Node* aNode )
468 {
469 //Mode is an optional attribute for specifying whether a block is incremental, non-incremental or a single register
470 std::string lModeAttr;
471
473 {
474 const defs::BlockReadWriteMode* const lMode = mModeLut.find(lModeAttr.c_str());
475 if (lMode == NULL)
476 {
477 throw exception::NodeAttributeIncorrectValue("Mode attribute for node with ID '" + aNode->mUid + "' has incorrect value '" + lModeAttr + "'");
478 }
479 else
480 aNode->mMode = *lMode;
481
482 if ( aNode->mMode == defs::INCREMENTAL )
483 {
484 //If a block is incremental it requires a size attribute
486 {
487 exception::IncrementalNodeRequiresSizeAttribute lExc;
488 log ( lExc , "Node " , Quote ( aNode->mUid ) , " has type " , Quote ( "INCREMENTAL" ) , ", which requires a " , Quote ( NodeTreeBuilder::mSizeAttribute ) , " attribute" );
489 throw lExc;
490 }
491 }
492 else if ( aNode->mMode == defs::NON_INCREMENTAL )
493 {
494 //If a block is non-incremental, then a size attribute is recommended
496 {
497 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." );
498 }
499 }
500 }
501 else if ( not aXmlNode.attribute ( NodeTreeBuilder::mSizeAttribute.c_str() ).empty() )
502 {
503 log ( Warning() , "Invalid combination of attributes for node " , Quote ( aNode->mUid ) , ": Size attribute specified, but mode missing, hence size ignored. Please specify mode here or remove the size attribute. Address table parser will throw an exception for this in future releases.");
504 }
505
506 }
507
508 void NodeTreeBuilder::setFirmwareInfo ( const pugi::xml_node& aXmlNode , Node* aNode )
509 {
510 //Address is an optional attribute for hierarchical addressing
511 std::string lFwInfoStr;
513
514 if ( lFwInfoStr.size() )
515 {
516 //parse the string into a NodeTreeFwInfoAttribute object
517 std::string::const_iterator lBegin ( lFwInfoStr.begin() );
518 std::string::const_iterator lEnd ( lFwInfoStr.end() );
520 boost::spirit::qi::phrase_parse ( lBegin , lEnd , mNodeTreeFirmwareInfoAttributeGrammar , boost::spirit::ascii::space , lFwInfo );
521 aNode->mFirmwareInfo.insert ( make_pair ( "type",lFwInfo.mType ) );
522
523 if ( lFwInfo.mArguments.size() )
524 {
525 aNode->mFirmwareInfo.insert ( lFwInfo.mArguments.begin() , lFwInfo.mArguments.end() );
526 }
527 }
528 }
529
530
531 void NodeTreeBuilder::addChildren ( const pugi::xml_node& aXmlNode , Node* aNode )
532 {
533 pugi::xml_node lXmlNode = aXmlNode.child ( "node" );
534
535 if ( aNode->mMode == defs::NON_INCREMENTAL )
536 {
537 if ( lXmlNode )
538 {
539 exception::BlockAccessNodeCannotHaveChild lExc;
540 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" );
541 throw lExc;
542 }
543 }
544 else
545 {
546 for ( ; lXmlNode; lXmlNode = lXmlNode.next_sibling ( "node" ) )
547 {
548 aNode->mChildren.push_back ( mNodeParser ( lXmlNode ) );
549 }
550
551 for (const auto& lChild: aNode->mChildren)
552 aNode->mChildrenMap.insert ( std::make_pair ( lChild->mUid , lChild ) );
553 }
554 }
555
556 void NodeTreeBuilder::calculateHierarchicalAddresses ( Node* aNode , const uint32_t& aAddr )
557 {
558 if ( aNode->mMode == defs::HIERARCHICAL )
559 {
560 if ( aNode->mChildren.size() == 0 )
561 {
562 aNode->mMode = defs::SINGLE;
563 }
564 else
565 {
566 // bool lAnyMasked( false );
567 bool lAllMasked ( true );
568
569 for (Node* lChild: aNode->mChildren)
570 {
571 if ( lChild->mMask == defs::NOMASK )
572 lAllMasked = false;
573
574 // else
575 // {
576 // lAnyMasked = true;
577 // }
578 }
579
580 // if( lAnyMasked && !lAllMasked )
581 // {
582 // log ( Error() , "Both masked and unmasked children found in branch " , Quote ( aNode->mUid ) );
583 // throw exception::// BothMaskedAndUnmaskedChildren();
584 // }
585
586 if ( lAllMasked )
587 {
588 aNode->mMode = defs::SINGLE;
589 }
590 }
591 }
592
593 if ( aNode->mMode == defs::INCREMENTAL )
594 {
595 uint64_t lTopAddr ( ( uint64_t ) ( aNode->mPartialAddr ) + ( uint64_t ) ( aNode->mSize-1 ) );
596
597 //Check that the requested block size does not extend outside register space
598 if ( lTopAddr >> 32 )
599 {
600 exception::ArraySizeExceedsRegisterBound lExc;
601 log ( lExc , "A block size of " , Integer ( aNode->mSize ) , " and a base address of " , Integer ( aNode->mAddr , IntFmt<hex,fixed>() ) , " exceeds bounds of address space" );
602 throw lExc;
603 }
604
605 /*
606 //Test for overlap with parent
607 if ( ( uint32_t ) ( lTopAddr ) & aAddr ) //should set the most significant bit of the child address and then AND this with the parent address
608 {
609 log ( Warning() , "The partial address of the top register in the current branch, " , Quote ( aNode->mUid ) , " , (" , Integer ( ( uint32_t ) ( lTopAddr ) , IntFmt<hex,fixed>() ) , ") overlaps with the partial address of the parent branch (" , Integer ( aAddr , IntFmt<hex,fixed>() ) , "). This might contradict the hierarchical design principal. For now this is a warning, but in the future this may be upgraded to throw an exception." );
610 }
611
612 }
613 else
614 {
615 //Test for overlap with parent
616 if ( aNode->mPartialAddr & aAddr ) //should set the most significant bit of the child address and then AND this with the parent address
617 {
618 log ( Warning() , "The partial address of the top register in the current branch, " , Quote ( aNode->mUid ) , " , (" , Integer ( aNode->mPartialAddr , IntFmt<hex,fixed>() ) , ") overlaps with the partial address of the parent branch (" , Integer ( aAddr , IntFmt<hex,fixed>() ) , "). This might contradict the hierarchical design principal. For now this is a warning, but in the future this may be upgraded to throw an exception." );
619 }
620 */
621 }
622
623 aNode->mAddr = aNode->mPartialAddr + aAddr;
624
625 for (Node* lChild: aNode->mChildren)
626 {
627 lChild->mParent = aNode;
628 calculateHierarchicalAddresses ( lChild , aNode->mAddr );
629 }
630
631 std::sort ( aNode->mChildren.begin() , aNode->mChildren.end() , detail::compareNodeAddr );
632 }
633
634
635 void NodeTreeBuilder::checkForAddressCollisions ( Node* aNode , const boost::filesystem::path& aPath )
636 {
637 std::vector<std::pair<const Node*, const Node*> > lOverlappingNodes = detail::getAddressOverlaps(*aNode);
638
639 if ( not lOverlappingNodes.empty() )
640 {
641 // Add username to the collisions report filepath if environment variable USER is defined
642 std::string lDirName("/tmp");
643 if (char* lUsername = std::getenv("USER"))
644 {
645 lDirName += "/" + std::string(lUsername);
646 }
647 lDirName += "/uhal";
648
649 boost::filesystem::path lDir ( lDirName );
650 lDir.make_preferred();
651
652 try{
653 if ( !boost::filesystem::is_directory ( lDir ) )
654 {
655 if ( boost::filesystem::create_directories ( lDir ) )
656 {
657 boost::filesystem::permissions( lDir , boost::filesystem::all_all );
658 }
659 else
660 {
661 log ( Error() , "Address overlaps observed - attempted and failed to create directory " , Quote ( lDirName ) );
662 return;
663 }
664
665 }
666 }
667 catch(const boost::filesystem::filesystem_error& e)
668 {
669 log ( Error() , "Address overlaps observed - failed to create directory " , Quote ( lDirName ) , " for report file; caught filesystem_error exception with what returning: ", e.what() );
670 return;
671 }
672
673 std::string lFilename ( aPath.string() );
674 boost::replace_all ( lFilename , "/" , "-" );
675 const std::string lReportPath( ( lDir / ( "OverlapReport" + lFilename + ".txt" ) ).string() );
676
677 if ( detail::writeNodeOverlapReport(lReportPath, lOverlappingNodes, "Overlap report for \"" + aPath.string() + "\".") )
678 {
679 log ( Warning() , "Address overlaps observed - report file written at " , Quote ( lReportPath ) );
680 }
681 else
682 {
683 log ( Error() , "Address overlaps observed - failed to create report file " , Quote ( lReportPath ) );
684 }
685 }
686 }
687
688
690 {
691 add
692 ( "r" , defs::READ )
693 ( "w" , defs::WRITE )
694 ( "read" , defs::READ )
695 ( "write" , defs::WRITE )
696 ( "rw" , defs::READWRITE )
697 ( "wr" , defs::READWRITE )
698 ( "readwrite" , defs::READWRITE )
699 ( "writeread" , defs::READWRITE )
700 ;
701 }
702
704
705
707 {
708 add
709 ( "single" , defs::SINGLE )
710 ( "block" , defs::INCREMENTAL )
711 ( "port" , defs::NON_INCREMENTAL )
712 ( "incremental" , defs::INCREMENTAL )
713 ( "non-incremental" , defs::NON_INCREMENTAL )
714 ( "inc" , defs::INCREMENTAL )
715 ( "non-inc" , defs::NON_INCREMENTAL )
716 ;
717 }
718
720
721}
bool empty() const
Definition: pugixml.cpp:5212
xml_parse_result load_buffer_inplace(void *contents, size_t size, unsigned int options=parse_default, xml_encoding encoding=encoding_auto)
Definition: pugixml.cpp:7142
xml_node child(const char_t *name) const
Definition: pugixml.cpp:5497
xml_node next_sibling() const
Definition: pugixml.cpp:5528
xml_attribute attribute(const char_t *name) const
Definition: pugixml.cpp:5507
Node * convertToClassType(Node *aNode)
static DerivedNodeFactory & getInstance()
Static method to retrieve the single instance of the class.
A heirarchical node for navigating heirarchical firmwares.
Definition: Node.hpp:85
std::unordered_map< std::string, std::string > mFirmwareInfo
parameters to infer the VHDL address decoding
Definition: Node.hpp:370
std::vector< Node * > mChildren
The direct children of the node.
Definition: Node.hpp:376
uint32_t mAddr
The register address with which this node is associated.
Definition: Node.hpp:343
defs::BlockReadWriteMode mMode
Whether the node represents a single register, a block of registers or a block-read/write port.
Definition: Node.hpp:350
uint32_t mMask
The mask to be applied if this node is a sub-field, rather than an entire register.
Definition: Node.hpp:346
std::unordered_map< std::string, Node * > mChildrenMap
Helper to assist look-up of a particular child node, given a name.
Definition: Node.hpp:379
std::string mUid
The Unique ID of this node.
Definition: Node.hpp:338
std::string mClassName
Class name used to construct the derived node type.
Definition: Node.hpp:364
std::string mDescription
Optional string which the user can specify.
Definition: Node.hpp:358
std::unordered_map< std::string, std::string > mParameters
Additional parameters of the node.
Definition: Node.hpp:367
defs::NodePermission mPermission
The read/write access permissions of this node.
Definition: Node.hpp:348
std::string mModule
The name of the module in which the current node resides.
Definition: Node.hpp:361
uint32_t mPartialAddr
The register address with which this node is associated.
Definition: Node.hpp:341
uint32_t mSize
The maximum size available to a block read/write.
Definition: Node.hpp:352
std::string mTags
Optional string which the user can specify.
Definition: Node.hpp:355
Node * mParent
The parent of the current node.
Definition: Node.hpp:373
A class to build a node tree from an address table file.
void setModule(const pugi::xml_node &aXmlNode, Node *aNode)
static NodeTreeBuilder & getInstance()
Static method to retrieve the single instance of the class.
static const std::string mModeAttribute
uhal::NodeTreeBuilder::permissions_lut mPermissionsLut
An instance of a look-up table that the boost qi parser uses for associating strings with enumerated ...
void setModeAndSize(const pugi::xml_node &aXmlNode, Node *aNode)
static const std::string mTagsAttribute
void setPermissions(const pugi::xml_node &aXmlNode, Node *aNode)
NodeTreeBuilder()
Default constructor This is private since only a single instance is to be created,...
Parser< Node * > mTopLevelNodeParser
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.
void setUid(const bool &aRequireId, const pugi::xml_node &aXmlNode, Node *aNode)
void setMask(const pugi::xml_node &aXmlNode, Node *aNode)
void setTags(const pugi::xml_node &aXmlNode, Node *aNode)
grammars::NodeTreeFirmwareinfoAttributeGrammar mNodeTreeFirmwareInfoAttributeGrammar
static const std::string mSizeAttribute
void setClassName(const pugi::xml_node &aXmlNode, Node *aNode)
static const std::string mParametersAttribute
static const std::string mModuleAttribute
std::deque< boost::filesystem::path > mFileCallStack
static std::shared_ptr< NodeTreeBuilder > mInstance
The single instance of the class.
void setAddr(const pugi::xml_node &aXmlNode, Node *aNode)
static const std::string mAddressAttribute
static const std::string mMaskAttribute
Parser< Node * > mNodeParser
void setDescription(const pugi::xml_node &aXmlNode, Node *aNode)
static const std::string mFirmwareInfo
Node * getNodeTree(const std::string &aFilenameExpr, const boost::filesystem::path &aPath)
Construct a node tree from file whose name is specified.
void checkForAddressCollisions(Node *aNode, const boost::filesystem::path &aPath)
uhal::NodeTreeBuilder::mode_lut mModeLut
An instance of a look-up table that the boost qi parser uses for associating strings with enumerated ...
void calculateHierarchicalAddresses(Node *aNode, const uint32_t &aAddr)
Propagate the addresses down through the hierarchical structure.
void clearAddressFileCache()
Clears address filename -> Node tree cache. NOT thread safe; for tread-safety, use ConnectionManager ...
static const std::string mClassAttribute
static const std::string mDescriptionAttribute
std::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 ...
grammars::NodeTreeParametersGrammar mNodeTreeParametersGrammar
virtual ~NodeTreeBuilder()
Destructor.
void setFirmwareInfo(const pugi::xml_node &aXmlNode, Node *aNode)
Node * plainNodeCreator(const bool &aRequireId, const pugi::xml_node &aXmlNode)
Node * moduleNodeCreator(const bool &aRequireId, const pugi::xml_node &aXmlNode)
Node * bitmaskNodeCreator(const bool &aRequireId, const pugi::xml_node &aXmlNode)
static const std::string mPermissionsAttribute
void setPars(const pugi::xml_node &aXmlNode, Node *aNode)
void addChildren(const pugi::xml_node &aXmlNode, Node *aNode)
static const std::string mIdAttribute
Node * build(const pugi::xml_node &aNode, const boost::filesystem::path &aAddressFilePath)
Rule for matching XML attributes.
Definition: XmlParser.hpp:145
Rule< R > & optional(const std::string &aStr)
Add an optional attribute to the rule.
Definition: XmlParser.hxx:140
Rule< R > & require(const std::string &aStr)
Add a required attribute to the rule.
Definition: XmlParser.hxx:110
BlockReadWriteMode
define whether transactions target a single register, a block of registers, a block-read/write port o...
Definition: definitions.hpp:53
NodePermission
define Read and Write permissions of a uhal Node
Definition: definitions.hpp:50
const uint32_t NOMASK
define what it means to have no mask
Definition: definitions.hpp:56
bool writeNodeOverlapReport(const std::string &aFilePath, const std::vector< std::pair< const Node *, const Node * > > &aNodes, const std::string &aHeader)
Definition: utilities.cpp:197
bool compareNodeAddr(const Node *aNodeL, const Node *aNodeR)
Definition: utilities.cpp:114
std::vector< std::pair< const Node *, const Node * > > getAddressOverlaps(const Node &aNode)
Definition: utilities.cpp:120
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.
Definition: files.cpp:63
template bool GetXMLattribute< false >(const pugi::xml_node &aNode, const std::string &aAttrName, std::string &aTarget)
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.
Definition: xml.cpp:57
void OpenFile(const std::string &aProtocol, const std::string &aFilenameExpr, const boost::filesystem::path &aParentPath, const detail::FileCallback_t &aCallback)
Given a protocol and either a URL or a linux shell expression, open the file and call the callback fu...
Definition: files.cpp:329
DebugLevel Debug
Definition: LogLevels.cpp:133
_Quote< T > Quote(const T &aT)
_Integer< T, IntFmt<> > Integer(const T &aT)
Forward declare a function which creates an instance of the ultra-lightweight wrapper from an integer...
ErrorLevel Error
Definition: LogLevels.cpp:61
void log(FatalLevel &aFatal, const T0 &aArg0)
Function to add a log entry at Fatal level.
Definition: log.hxx:18
WarningLevel Warning
Definition: LogLevels.cpp:79
InfoLevel Info
Definition: LogLevels.cpp:115
NoticeLevel Notice
Definition: LogLevels.cpp:97
constexpr int first(int i)
Implementation details for constexpr functions.
Definition: common.h:795
Annotation for arguments.
Definition: cast.h:1238
Empty struct which acts as a dummy variable for passing the formatting information around.
A look-up table that the boost qi parser uses for associating strings ("single","block",...
mode_lut()
The actual function that the boost qi parser uses for associating strings with enumerated permissions...
A look-up table that the boost qi parser uses for associating strings ("r","w","rw",...
permissions_lut()
The actual function that the boost qi parser uses for associating strings with enumerated permissions...
Struct to store the name and member variables within a node endpoint attribute when parsed by boost s...
NameValuePairVectorType mArguments
The member variable of the endpoint stored as "name1=val1;name2=val2;name3=val3".