-
Notifications
You must be signed in to change notification settings - Fork 89
Custom Plugins
To use the full capabilities of HAL, you can develop plugins yourself. Although we can use scripts to use the HAL API, sometimes the speed of C++ is necessary to achieve the desired performance. Here we show you how to develop your own plugins. Furthermore, we show how to include python bindings to the code.
This plugin contains the minimal amount of code to run and be used in HAL. This plugin just outputs "Hello World!" to the console and the python binding just returns the string "Hello World Py".
One can ether create all the necessary files from scratch or use the plugin-set-up-script provided with HAL. Just cd into your working directory for the plugin located at <Path to HAL>/plugins/<plugin name> and run <pathToHAL>/tools/new_plugin.py <name>. In the following documentation the plugin name is barebone.
This script creates the following directories and files in the current working directory.
├── CMakeLists.txt
├── include/barebone
│ └── barebone.h
├── python
│ └── python_bindings.cpp
└── src
└── barebone.cpp
So let's start by implementing the code necessary to produce the a basic HAL plugin. Let us create both the header and source file.
// include/barebone/barebone.h
#pragma once
#include "hal_core/defines.h"
#include "hal_core/plugin_system/plugin_interface_base.h"
#include "hal_core/utilities/result.h"
#include "hal_core/netlist/netlist.h"
namespace hal
{
class PLUGIN_API BarebonePlugin : public BasePluginInterface
{
public:
std::string get_name() const override;
std::string get_version() const override;
void initialize() override;
// this is a custom function used for the py binding
static void barebone_test();
};
} // namespace halHere we first include all the necessary files. Our new Plugin Class is called BarebonePlugin and needs to be extended from the BasePluginInterface class. There are a few methods every plugin needs like get_name. In this example we furthermore create a custom method called barebone_test. This function is later used to showcase the Python bindings.
All methods except barebone_test() are necessary functions of the provided BasePluginInterface.
The actual functionality is implemented in the barebone.cpp file.
// src/barebone.cpp
#include "barebone/barebone.h"
#include "boost/functional/hash.hpp"
#include "hal_core/netlist/gate.h"
#include "hal_core/netlist/module.h"
#include <deque>
namespace hal
{
extern std::unique_ptr<BasePluginInterface> create_plugin_instance()
{
return std::make_unique<BarebonePlugin>();
}
std::string BarebonePlugin::get_name() const
{
return std::string("barebone");
}
std::string BarebonePlugin::get_version() const
{
return std::string("0.1");
}
void BarebonePlugin::initialize()
{
}
void BarebonePlugin::barebone_test() {
std::cout << "Hello World!" << std::endl;
}
} // namespace halHere we add basic functionalities to the previous declared methods. Our custom barebone_test method just prints Hello World! to the console.
As HAL also brings a python shell for scripted analysis with it, you might want to expose your plugin to this environment. For this purpose, we use pybind11. The actual mapping is done in the python/python_bindings.cpp file.
// python/python_bindings.cpp
#include "hal_core/python_bindings/python_bindings.h"
#include "barebone/barebone.h"
#include "pybind11/operators.h"
#include "pybind11/pybind11.h"
#include "pybind11/stl.h"
#include "pybind11/stl_bind.h"
namespace py = pybind11;
namespace hal
{
// the name in PYBIND11_MODULE/PYBIND11_PLUGIN *MUST* match the filename of the output library (without extension),
// otherwise you will get "ImportError: dynamic module does not define module export function" when importing the module
#ifdef PYBIND11_MODULE
PYBIND11_MODULE(barebone, m)
{
m.doc() = "hal barebone python bindings";
#else
PYBIND11_PLUGIN(barebone)
{
py::module m("barebone", "hal barebone python bindings");
#endif
// define all exposed functions
py::class_<BarebonePlugin, RawPtrWrapper<BarebonePlugin>, BasePluginInterface>(m, "BarebonePlugin")
.def_property_readonly("name", &BarebonePlugin::get_name)
.def("get_name", &BarebonePlugin::get_name)
.def_property_readonly("version", &BarebonePlugin::get_version)
.def("get_version", &BarebonePlugin::get_version)
// this is an example function
.def_static(
"barebone_test",
[]() -> std::string {
// we can call for example functions of the plugin here
BarebonePlugin::barebone_test();
return "Hello World Py";
});
#ifndef PYBIND11_MODULE
return m.ptr();
#endif
}
} // namespace halHere we declare the python bindings. First we create a module for our functions. Here you can a custom documentation text in the upper #ifdef block. Just make sure to edit the module name from barebone to your name.
After that we declare all the exposed functions. Remember to change all References to your plugin class from BarebonePlugin to your name. With def_property_readonlywe can create read-only properties the plugin name or version. With defwe can create functions. Remember that these only work on instances of this class.With def_staicwe can create static functions for our class.
In all these cases we need to define a name under which the function is callable in python and a reference to the corresponding plugin method. We can also use unnamed lambda functions here as shown in the barebone_testfunction. Here we also call the corresponding plugin method but also execute further code. In this example we return the string Hello World Py. So when we call this function in python we write Hello World!to the console and return Hello World Py to the python script.
To build this plugin using HAL, we use the CMakeLists.txt file. The file should look something like this.
# CMakeLists.txt
option(PL_BAREBONE "PL_BAREBONE" OFF)
if(PL_BAREBONE OR BUILD_ALL_PLUGINS)
file(GLOB_RECURSE BAREBONE_INC ${CMAKE_CURRENT_SOURCE_DIR}/include/*.h)
file(GLOB_RECURSE BAREBONE_SRC ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp)
file(GLOB_RECURSE BAREBONE_PYTHON_SRC ${CMAKE_CURRENT_SOURCE_DIR}/python/*.cpp)
hal_add_plugin(barebone
SHARED
HEADER ${BAREBONE_INC}
SOURCES ${BAREBONE_SRC} ${BAREBONE_PYTHON_SRC}
PYDOC SPHINX_DOC_INDEX_FILE ${CMAKE_CURRENT_SOURCE_DIR}/documentation/barebone.rst
)
endif()Here PL_BAREBONEis used as a flag that can later be used to specify to cmake that we want to build this plugin. Change this to something unique (it is best to follow the hal standard of PL_<your plugin name in all capital letters>). You should edit the variables BAREBONE_INC, BAREBONE_SRC, BAREBONE_PYTHON_SRC accordingly. Here we just specify all the files we like to compile for this plugin. Remember to change the plugin name from bareboneto your name.
Now you can build HAL using cmake .. -DBUILD_ALL_PLUGINS=1 or better just cmake .. -DPL_BAREBONE=1 to build your newly created plugin. You can also call it using python as followed:
from hal_plugins.barebone import BarebonePlugin
output = BarebonePlugin.barebone_test()
print(output) # this prints the string "Hello World Py"