Skip to content

TODO

  • General class diagram walkthrough
  • Printing (BUTextIO)
  • RegisterHelper
  • IPBusRegisterHelper
  • IPBus Classes
  • Name autocompletion
  • How to use
  • How to write
  • DynamicLoader Magic

Class Hierarchy

Image

BUTool is a command-line application used for interacting with hardware through the use of hardware-specific plugin device classes.

CommandListBase and CommandList

The main BUTool class is CommandListBase, from which the templated class CommandList derives. CommandList implements the EvaluateCommand and AddCommand methods, which are the foundation of plugin device class functionality.

  • AddCommand(): This method is called in the plugin device class' LoadCommandList method, which is then called in the constructor of the device. The AddCommand() method takes in the command name string, command function pointer, a help string, and an optional auto-complete function pointer. The command (and it's alias, if defined via the AddCommandAlias() method) will be added to the private commandAndAliasList map, where the mapping between string and function is stored.

  • EvaluateCommand(): This CommandList method takes a vector of strings containing the command (or its alias) and its arguments and parses the arguments into the appropriate vector of strings or unsigned 64-bit integers, which are the two accepted parameters for BUTool and plugin device commands. After parsing the input vector, EvaluateCommand() invokes the desired command with the parsed arguments.

Launcher

The Launcher class provides generic commands for the standalone BUTool. Like any class deriving from CommandList, its command functions are defined privately in the class and mapped to strings via the inherited AddCommand() method.

BUTextIO and BUTextController (BUTextIO is a WIP)

These classes allow for printing formatted text (in a manner identical to printf) to multiple output streams at once. The BUTextIO class actually depends on a second class, BUTextController, not shown in the above inheritance diagram.

The BUTextController contains a vector of ostream pointers and several functions for manipulating that vector. The BUTextIO class itself has a vector of three of these controllers corresponding to three levels of "severity" - info, debug, and error. The info and debug controllers are instantiated with pointers to std::cout in their vectors, and the error controller with std::cerr.

Adding and removing ostream pointers

The method AddOutputStream() requires the user to specify the controller Level to which they want to add the ostream (INFO, DEBUG or ERROR) as well as a pointer to that ostream. Special consideration must be taken as to the lifetime of the ostream, in that if the ostream object goes out of scope, its pointer will not be removed from the relevant controller's vector automatically (see WIP features below). Therefore, in cases like a function where an ostream is created and then goes out of scope after the function returns, RemoveOutputStream() must be called on that controller's pointer before the function returns in order to prevent a segfault the next time Print() attempts to write to the streams in that controller's vector.

Printing:

Formatted printing is performed identically to a standard printf() call, with the exception of having to specify the BUTextController to which to direct the formatted text. This is done using the Level enum, where the possible levels are, as stated above, INFO, DEBUG, and ERROR.

For example, the command

Print(Level::INFO, "%s : 0x%08X\n", "Test", 0xDEADBEEF)

would print "Test : 0xDEADBEEF" to every stream in the INFO controller's vector of ostreams, which by default includes std::cout.

Usage with device classes:

Device classes that interact with the hardware do so through the uHAL library, wrapped in IPBus classes as described below. The results of some hardware access functions produce outputs which are now routed through BUTextIO via BUTool's RegisterHelper class, also described below.

In order for RegisterHelper to have access to BUTextIO functionality, any device class that relies on IPBus classes must call the BUTool::RegisterHelper::SetupTextIO() method in its constructor. Failure to call this method will cause an exception to be thrown upon attempted instantiation of the device class, serving as a reminder.

Device plugins, due to their inheritance from CommandListBase, should have access to all BUTextIO methods.

To be added / WIP features:

  • work on using smart pointers instead of raw pointers to ostream objects
  • this will help ensure that if/when an ostream object whose pointer is stored in the vector goes out of scope, it can be safely cleared from the vector before it can be dereferenced for printing purposes.
  • hopefully would take the responsibility off the device class designer to remember to delete the stream from the vector via the above RemoveOutputStream() method.

IPBusIO and IPBusConnection

The IPBusIO class acts as the bridge between uHAL and BUTool along with its associated plugin device classes by using uHAL to handle IPBus reads and writes. The IPBusConnection class, using functionality from uHAL, handles the creation of IPBus devices from the associated connection file and address tables which describe the communications protocol and address layout of the IPBus endpoints. The IPBusConnection class maintains the actual uhal::HwInterface object on which reads and writes are performed by IPBusIO, which accesses the hardware interface object via a pointer.

BUTool::RegisterHelper and IPBusRegHelper

The IPBusRegHelper class bridges the API for BUTool's RegisterHelper with the IPBusIO functions. It does so by locally overloading RegisterHelper commands with functions that directly call the equivalent IPBusIO functions, which actually perform the operations on the hardware via the uHAL library. For simplicity, RegisterHelper and IPBusIO therefore share the same function names and signatures.

Example:

To illustrate how IPBusRegHelper works to bridge RegisterHelper and IPBusIO, we can look at an example of reading a hardware register.

Image

Here, a device class (through inheritance from BUTool) issues a read command, interpreted as RegReadRegister(), for some performing a read on a register or group of registers. RegisterHelper calls its own function for dealing with reads, ReadWithOffsetHelper(), which in turn calls the IPBusIO command of the same name through the IPBusRegHelper API bridge.

If the command is successful, IPBusIO::RegReadRegister() returns the value at that register to IPBusRegHelper::RegReadRegister(), which returns it finally to BUTool's ReadWithOffsetHelper() function. RegisterHelper, via inheritance from BUTextIO, then prints the result to all output streams in the INFO level BUTextController's vector. Finally, both BUTool read commands will return CommandReturn::OK statuses, signaling to BUTool that the command was successfully executed.

Note: As mentioned previously, RegisterHelper can only use the Print() method inherited from BUTextIO if its function SetupTextIO() is called in the device class' constructor. This function performs a dynamic_cast between its this pointer and the BUTextIO class, which can only succeed if the setup function is called on instantiation of the device at runtime.