From ed9e0388450b766a9893f5bab55b4635ec47edd9 Mon Sep 17 00:00:00 2001 From: tangxifan Date: Thu, 6 Feb 2020 17:14:29 -0700 Subject: [PATCH] add functionality of LUT truth table fix-up --- .../annotation/vpr_clustering_annotation.cpp | 21 ++ .../annotation/vpr_clustering_annotation.h | 8 + .../base/openfpga_lut_truth_table_fixup.cpp | 185 ++++++++++++++++++ .../src/base/openfpga_lut_truth_table_fixup.h | 23 +++ openfpga/src/base/openfpga_setup_command.cpp | 18 ++ openfpga/src/utils/lut_utils.cpp | 93 +++++++++ openfpga/src/utils/lut_utils.h | 25 +++ openfpga/test_script/s298_k6_frac.openfpga | 3 + 8 files changed, 376 insertions(+) create mode 100644 openfpga/src/base/openfpga_lut_truth_table_fixup.cpp create mode 100644 openfpga/src/base/openfpga_lut_truth_table_fixup.h create mode 100644 openfpga/src/utils/lut_utils.cpp create mode 100644 openfpga/src/utils/lut_utils.h diff --git a/openfpga/src/annotation/vpr_clustering_annotation.cpp b/openfpga/src/annotation/vpr_clustering_annotation.cpp index 747978023..cd32fdeac 100644 --- a/openfpga/src/annotation/vpr_clustering_annotation.cpp +++ b/openfpga/src/annotation/vpr_clustering_annotation.cpp @@ -31,6 +31,16 @@ ClusterNetId VprClusteringAnnotation::net(const ClusterBlockId& block_id, const return net_names_.at(block_id).at(pin_index); } +bool VprClusteringAnnotation::is_truth_table_adapted(t_pb* pb) const { + /* Ensure that the block_id is in the list */ + return (block_truth_tables_.end() != block_truth_tables_.find(pb)); +} + +AtomNetlist::TruthTable VprClusteringAnnotation::truth_table(t_pb* pb) const { + VTR_ASSERT(true == is_truth_table_adapted(pb)); + return block_truth_tables_.at(pb); +} + /************************************************************************ * Public mutators ***********************************************************************/ @@ -46,4 +56,15 @@ void VprClusteringAnnotation::rename_net(const ClusterBlockId& block_id, const i net_names_[block_id][pin_index] = net_id; } +void VprClusteringAnnotation::adapt_truth_table(t_pb* pb, + const AtomNetlist::TruthTable& tt) { + /* Warn any override attempt */ + if (block_truth_tables_.end() != block_truth_tables_.find(pb)) { + VTR_LOG_WARN("Override the truth table for pb '%s' in clustering context annotation!\n", + pb->name); + } + + block_truth_tables_[pb] = tt; +} + } /* End namespace openfpga*/ diff --git a/openfpga/src/annotation/vpr_clustering_annotation.h b/openfpga/src/annotation/vpr_clustering_annotation.h index 5918a10b8..2951ef226 100644 --- a/openfpga/src/annotation/vpr_clustering_annotation.h +++ b/openfpga/src/annotation/vpr_clustering_annotation.h @@ -25,14 +25,22 @@ class VprClusteringAnnotation { public: /* Constructor */ VprClusteringAnnotation(); public: /* Public accessors */ + /* Xifan Tang: I created two functions for each data query in purpose! + * As this is an annotation, some net/block may be changed to invalid id + * In this case, return an invalid value does not mean that a net is not renamed + */ bool is_net_renamed(const ClusterBlockId& block_id, const int& pin_index) const; ClusterNetId net(const ClusterBlockId& block_id, const int& pin_index) const; + bool is_truth_table_adapted(t_pb* pb) const; + AtomNetlist::TruthTable truth_table(t_pb* pb) const; public: /* Public mutators */ void rename_net(const ClusterBlockId& block_id, const int& pin_index, const ClusterNetId& net_id); + void adapt_truth_table(t_pb* pb, const AtomNetlist::TruthTable& tt); private: /* Internal data */ /* Pair a regular pb_type to its physical pb_type */ std::map> net_names_; + std::map block_truth_tables_; }; } /* End namespace openfpga*/ diff --git a/openfpga/src/base/openfpga_lut_truth_table_fixup.cpp b/openfpga/src/base/openfpga_lut_truth_table_fixup.cpp new file mode 100644 index 000000000..9280e96bc --- /dev/null +++ b/openfpga/src/base/openfpga_lut_truth_table_fixup.cpp @@ -0,0 +1,185 @@ +/******************************************************************** + * This file includes functions to fix up the pb pin mapping results + * after routing optimization + *******************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_time.h" +#include "vtr_assert.h" +#include "vtr_log.h" + +/* Headers from vpr library */ +#include "vpr_utils.h" + +#include "pb_type_utils.h" +#include "lut_utils.h" +#include "openfpga_lut_truth_table_fixup.h" + +/* Include global variables of VPR */ +#include "globals.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Apply the fix-up to truth table of LUT according to its pin + * rotation status by packer + * + * Note: + * - pb must represents a LUT pb in the graph and it should be primitive + *******************************************************************/ +static +void fix_up_lut_atom_block_truth_table(const AtomContext& atom_ctx, + t_pb* pb, + const t_pb_routes& pb_route, + VprClusteringAnnotation& vpr_clustering_annotation, + const bool& verbose) { + t_pb_graph_node* pb_graph_node = pb->pb_graph_node; + t_pb_type* pb_type = pb->pb_graph_node->pb_type; + + VTR_ASSERT(LUT_CLASS == pb_type->class_type); + + for (int iport = 0; iport < pb_type->num_ports; ++iport) { + /* We only care about input ports whose pins are equivalent */ + if (IN_PORT != pb_type->ports[iport].type || true == pb_type->ports[iport].is_clock) { + continue; + } + if (pb_type->ports[iport].equivalent == PortEquivalence::NONE) { + continue; + } + /* Reach here, we have to apply a fix-up */ + AtomBlockId atom_blk = atom_ctx.nlist.find_block(pb->name); + VTR_ASSERT(atom_blk); + + /* Port exists (some LUTs may have no input and hence no port in the atom netlist) */ + AtomPortId atom_port = atom_ctx.nlist.find_atom_port(atom_blk, pb_type->ports[iport].model_port); + if (atom_port) { + continue; + } + + /* Find the pin rotation status and record it , + * Note that some LUT inputs may not be used, we set them to be open by default + */ + std::vector rotated_pin_map(pb_type->ports[iport].num_pins, -1); + for (int ipin = 0; ipin < pb_type->ports[iport].num_pins; ++ipin) { + int node_index = pb_graph_node->input_pins[iport][ipin].pin_count_in_cluster; + if (pb_route.count(node_index)) { + /* The pin is mapped to a net, find the original pin in the atom netlist */ + AtomNetId atom_net = pb_route[node_index].atom_net_id; + + VTR_ASSERT(atom_net); + + for (AtomPinId atom_pin : atom_ctx.nlist.port_pins(atom_port)) { + + AtomNetId atom_pin_net = atom_ctx.nlist.pin_net(atom_pin); + + if (atom_pin_net == atom_net) { + rotated_pin_map[ipin] = atom_ctx.nlist.pin_port_bit(atom_pin); + break; + } + } + } + } + + /* We can apply truth table adaption now + * For unused inputs : insert dont care + * For used inputs : find the bit in the truth table rows and move it by the given mapping + */ + const AtomNetlist::TruthTable& orig_tt = atom_ctx.nlist.block_truth_table(atom_blk); + const AtomNetlist::TruthTable& adapt_tt = lut_truth_table_adaption(orig_tt, rotated_pin_map); + vpr_clustering_annotation.adapt_truth_table(pb, adapt_tt); + + /* Print info is in the verbose mode */ + VTR_LOGV(verbose, "Original truth table\n"); + for (const std::string& tt_line : truth_table_to_string(orig_tt)) { + VTR_LOGV(verbose, "\t%s\n", tt_line.c_str()); + } + VTR_LOGV(verbose, "\n"); + VTR_LOGV(verbose, "Adapt truth table\n"); + VTR_LOGV(verbose, "-----------------\n"); + for (const std::string& tt_line : truth_table_to_string(adapt_tt)) { + VTR_LOGV(verbose, "\t%s\n", tt_line.c_str()); + } + VTR_LOGV(verbose, "\n"); + } +} + +/******************************************************************** + * This function recursively visits the pb graph until we reach a + * LUT pb_type (primitive node in the pb_graph with a class type + * of LUT_CLASS + * Once we find a LUT node, we will apply the fix-up + *******************************************************************/ +static +void rec_adapt_lut_pb_tt(const AtomContext& atom_ctx, + t_pb* pb, + const t_pb_routes& pb_route, + VprClusteringAnnotation& vpr_clustering_annotation, + const bool& verbose) { + t_pb_graph_node* pb_graph_node = pb->pb_graph_node; + + /* If we reach a primitive pb_graph node, we return */ + if (true == is_primitive_pb_type(pb_graph_node->pb_type)) { + if (LUT_CLASS == pb_graph_node->pb_type->class_type) { + /* Do fix-up here */ + fix_up_lut_atom_block_truth_table(atom_ctx, pb, pb_route, vpr_clustering_annotation, verbose); + } + return; + } + + /* Recursively visit all the used pbs in the graph */ + t_mode* mapped_mode = &(pb_graph_node->pb_type->modes[pb->mode]); + for (int ipb = 0; ipb < mapped_mode->num_pb_type_children; ++ipb) { + /* Each child may exist multiple times in the hierarchy*/ + for (int jpb = 0; jpb < mapped_mode->pb_type_children[ipb].num_pb; ++jpb) { + /* See if we still have any pb children to walk through */ + if ((pb->child_pbs[ipb] != nullptr) && (pb->child_pbs[ipb][jpb].name != nullptr)) { + rec_adapt_lut_pb_tt(atom_ctx, &(pb->child_pbs[ipb][jpb]), pb_route, vpr_clustering_annotation, verbose); + } + } + } +} + +/******************************************************************** + * Main function to fix up truth table for each LUT used in FPGA + * This function will walk through each clustered block + *******************************************************************/ +static +void update_lut_tt_with_post_packing_results(const AtomContext& atom_ctx, + const ClusteringContext& clustering_ctx, + VprClusteringAnnotation& vpr_clustering_annotation, + const bool& verbose) { + for (auto blk_id : clustering_ctx.clb_nlist.blocks()) { + rec_adapt_lut_pb_tt(atom_ctx, + clustering_ctx.clb_nlist.block_pb(blk_id), + clustering_ctx.clb_nlist.block_pb(blk_id)->pb_route, + vpr_clustering_annotation, verbose); + } +} + + +/******************************************************************** + * Top-level function to fix up the lut truth table results after packing is done + * The problem comes from a mismatch between the packing results and + * original truth tables in users' BLIF file + * As LUT inputs are equivalent in nature, the router of packer will try + * to swap the net mapping among these pins so as to achieve best + * routing optimization. + * However, it will cause the original truth table out-of-date when packing is done. + * This function aims to fix the mess after packing so that the truth table + * can be synchronized + *******************************************************************/ +void lut_truth_table_fixup(OpenfpgaContext& openfpga_context, + const Command& cmd, const CommandContext& cmd_context) { + + vtr::ScopedStartFinishTimer timer("Fix up LUT truth tables after packing optimization"); + + CommandOptionId opt_verbose = cmd.option("verbose"); + + /* Apply fix-up to each packed block */ + update_lut_tt_with_post_packing_results(g_vpr_ctx.atom(), + g_vpr_ctx.clustering(), + openfpga_context.mutable_vpr_clustering_annotation(), + cmd_context.option_enable(cmd, opt_verbose)); +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/base/openfpga_lut_truth_table_fixup.h b/openfpga/src/base/openfpga_lut_truth_table_fixup.h new file mode 100644 index 000000000..f11abc7fb --- /dev/null +++ b/openfpga/src/base/openfpga_lut_truth_table_fixup.h @@ -0,0 +1,23 @@ +#ifndef OPENFPGA_LUT_TRUTH_TABLE_FIXUP_H +#define OPENFPGA_LUT_TRUTH_TABLE_FIXUP_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include "command.h" +#include "command_context.h" +#include "openfpga_context.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void lut_truth_table_fixup(OpenfpgaContext& openfpga_context, + const Command& cmd, const CommandContext& cmd_context); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/base/openfpga_setup_command.cpp b/openfpga/src/base/openfpga_setup_command.cpp index 2a1edacbc..629300bf2 100644 --- a/openfpga/src/base/openfpga_setup_command.cpp +++ b/openfpga/src/base/openfpga_setup_command.cpp @@ -6,6 +6,7 @@ #include "openfpga_read_arch.h" #include "openfpga_link_arch.h" #include "openfpga_pb_pin_fixup.h" +#include "openfpga_lut_truth_table_fixup.h" #include "check_netlist_naming_conflict.h" #include "openfpga_setup_command.h" @@ -100,6 +101,23 @@ void add_openfpga_setup_commands(openfpga::Shell& shell) { cmd_dependency_pb_pin_fixup.push_back(shell_cmd_read_arch_id); cmd_dependency_pb_pin_fixup.push_back(shell_cmd_vpr_id); shell.set_command_dependency(shell_cmd_pb_pin_fixup_id, cmd_dependency_pb_pin_fixup); + + /******************************** + * Command 'lut_truth_table_fixup' + */ + Command shell_cmd_lut_truth_table_fixup("lut_truth_table_fixup"); + /* Add an option '--verbose' */ + shell_cmd_lut_truth_table_fixup.add_option("verbose", false, "Show verbose outputs"); + + /* Add command 'lut_truth_table_fixup' to the Shell */ + ShellCommandId shell_cmd_lut_truth_table_fixup_id = shell.add_command(shell_cmd_lut_truth_table_fixup, "Fix up the truth table of Look-Up Tables due to pin swapping during packing stage"); + shell.set_command_class(shell_cmd_lut_truth_table_fixup_id, openfpga_setup_cmd_class); + shell.set_command_execute_function(shell_cmd_lut_truth_table_fixup_id, lut_truth_table_fixup); + /* The 'pb_pin_fixup' command should NOT be executed before 'read_openfpga_arch' and 'vpr' */ + std::vector cmd_dependency_lut_truth_table_fixup; + cmd_dependency_lut_truth_table_fixup.push_back(shell_cmd_read_arch_id); + cmd_dependency_lut_truth_table_fixup.push_back(shell_cmd_vpr_id); + shell.set_command_dependency(shell_cmd_lut_truth_table_fixup_id, cmd_dependency_lut_truth_table_fixup); } } /* end namespace openfpga */ diff --git a/openfpga/src/utils/lut_utils.cpp b/openfpga/src/utils/lut_utils.cpp new file mode 100644 index 000000000..505502782 --- /dev/null +++ b/openfpga/src/utils/lut_utils.cpp @@ -0,0 +1,93 @@ +/******************************************************************** + * This file includes most utilized functions to manipulate LUTs, + * especially their truth tables, in the OpenFPGA context + *******************************************************************/ +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_log.h" + +#include "lut_utils.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * This function aims to adapt the truth table to a mapped physical LUT + * subject to a pin rotation map + * The modification is applied to line by line + * - For unused inputs : insert dont care + * - For used inputs : find the bit in the truth table rows and move it by the given mapping + * + * The rotated pin map is the reference to adapt the truth table. + * Each element of the map represents the input index in the original truth table + * The sequence of the rotate pin map is the final sequence of how + * each line of the original truth table should be shuffled + * Example: + * output_value(we do not modify) + * | + * v + * Truth table line: 00111 + * rotated_pin_map: 2310 + * Adapt truth table line: 11001 + *******************************************************************/ +AtomNetlist::TruthTable lut_truth_table_adaption(const AtomNetlist::TruthTable& orig_tt, + const std::vector& rotated_pin_map) { + AtomNetlist::TruthTable tt; + + for (auto row : orig_tt) { + VTR_ASSERT(row.size() - 1 == rotated_pin_map.size()); + + std::vector tt_line; + /* We do not care about the last digit, which is the output value */ + for (size_t i = 0; i < row.size() - 1; ++i) { + if (-1 == rotated_pin_map[i]) { + tt_line.push_back(vtr::LogicValue::DONT_CARE); + } else { + /* Ensure we never access the last digit, i.e., the output value! */ + VTR_ASSERT((size_t)rotated_pin_map[i] < row.size() - 1); + tt_line.push_back(row[rotated_pin_map[i]]); + } + } + + /* Do not miss the last digit in the final result */ + tt_line.push_back(row.back()); + tt.push_back(tt_line); + } + + return tt; +} + +/******************************************************************** + * Convert a truth table to strings, which are ready to be printed out + *******************************************************************/ +std::vector truth_table_to_string(const AtomNetlist::TruthTable& tt) { + std::vector tt_str; + for (auto row : tt) { + std::string row_str; + for (size_t i = 0; i < row.size(); ++i) { + /* Add a gap between inputs and outputs */ + if (i == row.size() - 1) { + row_str += std::string(" "); + } + switch (row[i]) { + case vtr::LogicValue::TRUE: + row_str += std::string("1"); + break; + case vtr::LogicValue::FALSE: + row_str += std::string("0"); + break; + case vtr::LogicValue::DONT_CARE: + row_str += std::string("-"); + break; + default: + VTR_ASSERT_MSG(false, "Valid single-output cover logic value"); + } + } + tt_str.push_back(row_str); + } + + return tt_str; +} + +} /* end namespace openfpga */ + diff --git a/openfpga/src/utils/lut_utils.h b/openfpga/src/utils/lut_utils.h new file mode 100644 index 000000000..800c1fc59 --- /dev/null +++ b/openfpga/src/utils/lut_utils.h @@ -0,0 +1,25 @@ +#ifndef LUT_UTILS_H +#define LUT_UTILS_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include +#include +#include "atom_netlist.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +AtomNetlist::TruthTable lut_truth_table_adaption(const AtomNetlist::TruthTable& orig_tt, + const std::vector& rotated_pin_map); + +std::vector truth_table_to_string(const AtomNetlist::TruthTable& tt); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/test_script/s298_k6_frac.openfpga b/openfpga/test_script/s298_k6_frac.openfpga index 9681918c4..f4a5bc979 100644 --- a/openfpga/test_script/s298_k6_frac.openfpga +++ b/openfpga/test_script/s298_k6_frac.openfpga @@ -13,5 +13,8 @@ check_netlist_naming_conflict --fix --report ./netlist_renaming.xml # Apply fix-up to clustering nets based on routing results pb_pin_fixup --verbose +# Apply fix-up to Look-Up Table truth tables based on packing results +lut_truth_table_fixup --verbose + # Finish and exit OpenFPGA exit