Skip to content

Commit

Permalink
✨ ABC Callback (#605)
Browse files Browse the repository at this point in the history
* 🔧 Update the CMake to enable the detection and inclusion of ABC

* ✨ First draft of an ABC callback command that enables its utilization directly from within the CLI

* 🎨 Fixed missing includes and consistency

* 🎨 Added error handling and docstrings

* 📝 Command description

* 📝 Documentation for enabling the ABC callback and its usage

* 📝 More notes on ABC installation

* 🐛 Limit ABC callback to AIGs and XAGs due to issues with MIGs and technology networks

* 🐛 Add the `-s` flag to ABC's `write_aiger` call to retain input/output names

* 🔥 Remove debug output

* 📝 Add a help string to the ABC call

* 📝 Update pyfiction docstrings

Signed-off-by: GitHub Actions <actions@github.com>

* 🔊 Silence ABC output

* ✨ Add flags to omit reading, strashing, or writing in ABC

* 🏗️ Move `equiv` command to the `logic` CLI category

* 📝 Add a short mention about ABC to the README

* 🎨 Incorporated pre-commit fixes

---------

Signed-off-by: GitHub Actions <actions@github.com>
Signed-off-by: Marcel Walter <marcel.walter@tum.de>
Co-authored-by: GitHub Actions <actions@github.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
  • Loading branch information
3 people authored Jan 9, 2025
1 parent 65ccc40 commit f2264fd
Show file tree
Hide file tree
Showing 9 changed files with 333 additions and 32 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -176,9 +176,9 @@ physical simulation.

### Logic Synthesis

For logic synthesis, _fiction_ relies on the [mockturtle library](https://github.com/lsils/mockturtle) that offers a
multitude of logic network types and optimization algorithms. Logic synthesis can be performed in external tools and
resulting Verilog/AIGER/BLIF/... files can be parsed by _fiction_. Alternatively, since _mockturtle_ is included in
For logic synthesis, _fiction_ utilizes [ABC](https://github.com/berkeley-abc/abc) and the [mockturtle library](https://github.com/lsils/mockturtle) that
offer a multitude of logic network types and optimization algorithms. Logic synthesis can be performed in external tools
and resulting Verilog/AIGER/BLIF/... files can be parsed by _fiction_. Alternatively, since _mockturtle_ is included in
_fiction_, synthesis can be applied in the same evaluation script.

### Physical Design
Expand Down
74 changes: 52 additions & 22 deletions cli/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,35 +13,65 @@ target_link_libraries(fiction PRIVATE libfiction alice)

# Logic synthesis flow
option(FICTION_LOGIC_SYNTHESIS_FLOW "Enable the logic synthesis flow for the fiction CLI" ON)
if(FICTION_LOGIC_SYNTHESIS_FLOW)
target_compile_definitions(fiction PRIVATE FICTION_LOGIC_SYNTHESIS_FLOW)
endif()
if (FICTION_LOGIC_SYNTHESIS_FLOW)
target_compile_definitions(fiction PRIVATE FICTION_LOGIC_SYNTHESIS_FLOW)
endif ()

# Physical design flow
option(FICTION_PHYSICAL_DESIGN_FLOW "Enable the physical design flow for the fiction CLI" ON)
if(FICTION_PHYSICAL_DESIGN_FLOW)
target_compile_definitions(fiction PRIVATE FICTION_PHYSICAL_DESIGN_FLOW)
endif()
if (FICTION_PHYSICAL_DESIGN_FLOW)
target_compile_definitions(fiction PRIVATE FICTION_PHYSICAL_DESIGN_FLOW)
endif ()

# Physical simulation flow
option(FICTION_SIMULATION_FLOW "Enable the physical simulation flow for the fiction CLI" ON)
if(FICTION_SIMULATION_FLOW)
target_compile_definitions(fiction PRIVATE FICTION_SIMULATION_FLOW)
endif()
if (FICTION_SIMULATION_FLOW)
target_compile_definitions(fiction PRIVATE FICTION_SIMULATION_FLOW)
endif ()

# If the logic synthesis flow is enabled, we can enable ABC as a callback
if (FICTION_LOGIC_SYNTHESIS_FLOW)
# Enable ABC
option(FICTION_ABC "Find, include, and utilize ABC. It needs to be installed manually." OFF)
if (FICTION_ABC)
message(STATUS "Usage of the Z3 solver was enabled. Make sure that it is installed on your system!")
# Option for a user-defined path to ABC
set(ABC_ROOT "" CACHE STRING "Path to the ABC directory")

find_program(
ABC_BINARY abc
HINTS
${ABC_ROOT}
$ENV{HOME}/.local/bin
/usr/local/bin
/usr/bin
/opt/abc/bin
ENV PATH
DOC "Path to the ABC executable"
)
if (ABC_BINARY)
message(STATUS "Found ABC binary: ${ABC_BINARY}")
target_compile_definitions(fiction PRIVATE FICTION_ABC)
target_compile_definitions(fiction PRIVATE ABC_EXECUTABLE="${ABC_BINARY}")
else ()
message(FATAL_ERROR "ABC not found. Please specify `ABC_ROOT` or ensure ABC is in your PATH.")
endif ()
endif ()
endif ()

# Strip the executable if we are in Release mode
if(CMAKE_BUILD_TYPE STREQUAL "Release")
if(CMAKE_STRIP)
add_custom_command(
TARGET fiction
POST_BUILD
COMMAND ${CMAKE_STRIP} $<TARGET_FILE:fiction>)
else()
message(
WARNING
"Strip command is not available. The executables will not be stripped.")
endif()
endif()
if (CMAKE_BUILD_TYPE STREQUAL "Release")
if (CMAKE_STRIP)
add_custom_command(
TARGET fiction
POST_BUILD
COMMAND ${CMAKE_STRIP} $<TARGET_FILE:fiction>)
else ()
message(
WARNING
"Strip command is not available. The executables will not be stripped.")
endif ()
endif ()

# Package the CLI executable
include(../cmake/PackageProject.cmake)
Expand All @@ -54,7 +84,7 @@ fiction_package_project(TARGETS fiction fiction_options fiction_warnings)
# out potential ABI related issues before they start, while helping you track a
# build to a specific GIT SHA
set(CPACK_PACKAGE_FILE_NAME
"${CMAKE_PROJECT_NAME}-${CMAKE_PROJECT_VERSION}-${GIT_SHORT_SHA}-${CMAKE_SYSTEM_NAME}-${CMAKE_BUILD_TYPE}-${CMAKE_CXX_COMPILER_ID}-${CMAKE_CXX_COMPILER_VERSION}"
"${CMAKE_PROJECT_NAME}-${CMAKE_PROJECT_VERSION}-${GIT_SHORT_SHA}-${CMAKE_SYSTEM_NAME}-${CMAKE_BUILD_TYPE}-${CMAKE_CXX_COMPILER_ID}-${CMAKE_CXX_COMPILER_VERSION}"
)

include(CPack)
1 change: 1 addition & 0 deletions cli/cmd/io/read.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <alice/alice.hpp>
#include <fmt/format.h>

#include <filesystem>
#include <memory>
#include <string>

Expand Down
225 changes: 225 additions & 0 deletions cli/cmd/logic/abc.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
//
// Created by marcel on 04.12.24.
//

#ifndef FICTION_CMD_ABC_HPP
#define FICTION_CMD_ABC_HPP

#if (FICTION_ABC)

#include <fiction/types.hpp>

#include <alice/alice.hpp>
#include <mockturtle/io/write_aiger.hpp>

#include <cstdlib>
#include <filesystem>
#include <iostream>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <variant>

namespace alice
{
/**
* Callback command for ABC. Calls the ABC binary with a given command string and the current logic network in store.
*
* Internally, ABC performs a `read <filename>; strash; <command>; write_aiger <filename>` operation.
*/
class abc_command : public command
{
public:
/**
* Standard constructor. Adds descriptive information, options, and flags.
*
* @param e alice::environment that specifies stores etc.
*/
explicit abc_command(const environment::ptr& e) :
command(
e,
"ABC callback for logic synthesis and optimization. The command `abc -c \"<command>\"` will launch ABC "
"and execute the given command string. Internally, it writes the current AIG or XAG to a temporary "
"AIGER file, have ABC parse this file as an AIG using `read <filename>; strash`, and then execute the "
"given command string. Finally, ABC will write the result back to the temporary file using "
"`write_aiger -s <filename>`, which will be read back into the fiction CLI as a new AIG.")
{
add_option("--command,-c", abc_command_str, "Command to pass to ABC")->required();
add_flag("--dont_read,-r", "Do not read the network into ABC, only execute the command");
add_flag("--dont_strash,-s", "Do not strash the network before executing the command");
add_flag("--dont_write,-w", "Do not write the network back to the CLI after executing the command");
}

protected:
/**
* Function to perform the synth call. Generates a logic network from a truth table.
*/
void execute() override
{
if constexpr (ABC == nullptr)
{
env->out() << "[e] `ABC_EXECUTABLE` environment variable is not set. Cannot launch ABC." << std::endl;
return;
}

const auto& s = store<fiction::logic_network_t>();

// error case: empty logic network store
if (s.empty())
{
env->out() << "[w] no logic network in store" << std::endl;
return;
}

constexpr auto is_tech_ntk = []([[maybe_unused]] auto&& ntk_ptr) -> bool
{ return std::is_same_v<typename std::decay_t<decltype(ntk_ptr)>::element_type, fiction::tec_nt>; };

if (std::visit(is_tech_ntk, s.current()))
{
env->out() << "[w] ABC callback does not support technology networks. Use AIGs or XAGs instead."
<< std::endl;
return;
}

constexpr auto is_maj_ntk = []([[maybe_unused]] auto&& ntk_ptr) -> bool
{ return std::is_same_v<typename std::decay_t<decltype(ntk_ptr)>::element_type, fiction::mig_nt>; };

if (std::visit(is_maj_ntk, s.current()))
{
env->out() << "[w] ABC callback does not support MIGs. Use AIGs or XAGs instead." << std::endl;
return;
}

try
{
const auto fiction_network_path = write_current_network_to_temp_file();
const auto abc_network_path = abc_output_temp_file();

const auto read_cmd =
fmt::format("{}", is_set("dont_read") ? "" : fmt::format("read {}; ", fiction_network_path.string()));
const auto strash_cmd = is_set("dont_strash") ? "" : "strash; ";
const auto write_cmd =
is_set("dont_write") ? "" : fmt::format("; write_aiger -s {}", abc_network_path.string());

// call the ABC binary
const auto abc_call =
fmt::format("{0} -q \"{1}{2}{3}{4}\"", ABC, read_cmd, strash_cmd, abc_command_str, write_cmd);

if (const auto ret = std::system(abc_call.c_str()); ret != 0)
{
env->out() << "[e] Failed to execute ABC command." << std::endl;
return;
}

fiction::network_reader<fiction::aig_ptr> reader{abc_network_path.string(), env->out()};

store<fiction::logic_network_t>().extend() = reader.get_networks().front();
}
catch (const std::exception& e)
{
env->out() << fmt::format("[e] {}", e.what()) << std::endl;
}
}

private:
/**
* Path to the ABC executable.
*/
static constexpr auto ABC = ABC_EXECUTABLE;
/**
* Command string to pass to ABC.
*/
std::string abc_command_str{};

/**
* Get the temporary directory depending on the platform.
*
* @return Path to the temporary directory.
*/
static std::filesystem::path get_temp_directory()
{
// Check common environment variables for the temporary directory
if (auto* const tempdir = std::getenv("TMPDIR"); tempdir != nullptr)
{
return {tempdir}; // Linux + macOS
}
if (auto* const temp = std::getenv("TEMP"); temp != nullptr)
{
return {temp}; // Windows
}
if (auto* const tmp = std::getenv("TMP"); tmp != nullptr)
{
return {tmp}; // Windows fallback
}

// fallback: standard platform-specific defaults
#ifdef _WIN32
return {"C:\\Windows\\Temp"};
#else
return {"/tmp"};
#endif
}
/**
* Returns a path to "<temp>/fiction" where <temp> is the system's temporary directory. The "fiction" folder is
* created if it doesn't exist.
*
* @return Path to the "<temp>/fiction" directory.
*/
static std::filesystem::path get_temp_fiction_directory()
{
// get the temporary directory
const auto temp_dir = get_temp_directory();

// define the "fiction" directory path
const auto fiction_dir = temp_dir / "fiction";

// create the directory if it doesn't exist
if (!std::filesystem::exists(fiction_dir))
{
if (!std::filesystem::create_directory(fiction_dir))
{
throw std::runtime_error("Failed to create directory: " + fiction_dir.string());
}
}

return fiction_dir;
}
/**
* Writes the current logic network in store to a temporary file in AIGER format and returns the path to the file.
*
* @return Path to a temporary AIGER file where the current network is stored.
*/
std::filesystem::path write_current_network_to_temp_file() const
{
const auto write = [](auto&& ntk_ptr) -> std::filesystem::path
{
const auto temp_file = get_temp_fiction_directory() / fmt::format("{}.aig", ntk_ptr->get_network_name());

mockturtle::write_aiger(*ntk_ptr, temp_file.string());

return temp_file;
};

return std::visit(write, store<fiction::logic_network_t>().current());
}
/**
* Returns the path to the temporary file where ABC is supposed to write its output.
*
* @return Path to the temporary file where ABC is supposed to write its output.
*/
std::filesystem::path abc_output_temp_file() const
{
const auto get_name = [](auto&& ntk_ptr) -> std::string { return ntk_ptr->get_network_name(); };

return get_temp_fiction_directory() /
fmt::format("{}.aig", std::visit(get_name, store<fiction::logic_network_t>().current()));
}
};

ALICE_ADD_COMMAND(abc, "Logic")

} // namespace alice

#endif // FICTION_ABC

#endif // FICTION_CMD_ABC_HPP
8 changes: 4 additions & 4 deletions cli/cmd/physical_design/exact.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
// Created by marcel on 06.01.20.
//

#if (FICTION_Z3_SOLVER)

#ifndef FICTION_CMD_EXACT_HPP
#define FICTION_CMD_EXACT_HPP

#if (FICTION_Z3_SOLVER)

#include <fiction/algorithms/physical_design/exact.hpp>
#include <fiction/layouts/clocking_scheme.hpp>
#include <fiction/types.hpp>
Expand Down Expand Up @@ -244,6 +244,6 @@ ALICE_ADD_COMMAND(exact, "Physical Design")

} // namespace alice

#endif // FICTION_CMD_EXACT_HPP

#endif // FICTION_Z3_SOLVER

#endif // FICTION_CMD_EXACT_HPP
3 changes: 2 additions & 1 deletion cli/commands.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@

// logic synthesis commands
#ifdef FICTION_LOGIC_SYNTHESIS_FLOW
#include "cmd/logic/abc.hpp"
#include "cmd/logic/akers.hpp"
#include "cmd/logic/balance.hpp"
#include "cmd/logic/fanouts.hpp"
Expand All @@ -32,6 +33,7 @@
#include "cmd/logic/miginvprop.hpp"
#include "cmd/logic/random.hpp"
#include "cmd/logic/simulate.hpp"
#include "cmd/verification/equiv.hpp"
#endif

// physical design and validation commands
Expand All @@ -46,7 +48,6 @@
#include "cmd/technology/cell.hpp"
#include "cmd/technology/energy.hpp"
#include "cmd/verification/check.hpp"
#include "cmd/verification/equiv.hpp"
#endif

// physical simulation commands
Expand Down
Loading

0 comments on commit f2264fd

Please sign in to comment.