[lib] developing fabric key assistant

This commit is contained in:
tangxifan 2023-08-26 12:54:12 -07:00
parent 74924bfaf8
commit adae7392e5
6 changed files with 328 additions and 0 deletions

View File

@ -20,6 +20,7 @@ set_target_properties(libfabrickey PROPERTIES PREFIX "") #Avoid extra 'lib' pref
#Specify link-time dependancies
target_link_libraries(libfabrickey
libopenfpgautil
libopenfpgashell
libarchopenfpga
libvtrutil
libpugiutil)

View File

@ -55,6 +55,35 @@ std::vector<FabricSubKeyId> FabricKey::sub_keys(
/************************************************************************
* Public Accessors : Basic data query
***********************************************************************/
size_t FabricKey::num_regions() const {
return region_ids_.size();
}
size_t FabricKey::num_keys() const {
return key_ids_.size();
}
std::vector<FabricKeyId> find_key_by_alias(const std::string& alias) const {
/* Throw warning on empty alias which may cause unexpected results: whole key is dumped! */
if (alias.empty()) {
VTR_LOG_WARN("Empty alias is given! This may cause unexpected results, i.e., a whole data base is dumped!\n");
}
size_t num_found_keys = 0;
for (FabricKeyId key_id : key_ids_) {
if (key_alias(key_id) == alias) {
num_found_keys++;
}
}
std::vector<FabricKeyId> found_keys;
found_keys.reserve(num_found_keys);
for (FabricKeyId key_id : key_ids_) {
if (key_alias(key_id) == alias) {
found_keys.push_back(key_id);
}
}
return found_keys;
}
std::vector<FabricKeyId> FabricKey::region_keys(
const FabricRegionId& region_id) const {
/* validate the region_id */

View File

@ -75,6 +75,8 @@ class FabricKey {
const FabricKeyModuleId& module_id) const;
public: /* Public Accessors: Basic data query */
size_t num_regions() const;
size_t num_keys() const;
/* Access all the keys of a region */
std::vector<FabricKeyId> region_keys(const FabricRegionId& region_id) const;
/* Access the name of a key */
@ -86,6 +88,9 @@ class FabricKey {
/* Access the coordinate of a key */
vtr::Point<int> key_coordinate(const FabricKeyId& key_id) const;
/** @brief Find valid key ids for a given alias. Note that you should NOT send an empty alias which may cause a complete list of key ids to be returned (extremely inefficent and NOT useful). Suggest to check if the existing fabric key contains valid alias for each key before calling this API!!! */
std::vector<FabricKeyId> find_key_by_alias(const std::string& alias) const;
/* Check if there are any keys */
bool empty() const;

View File

@ -0,0 +1,102 @@
/************************************************************************
* Check functions for the content of fabric key to avoid conflicts with
* other data structures
* These functions are not universal methods for the FabricKey class
* They are made to ease the development in some specific purposes
* Please classify such functions in this file
***********************************************************************/
#include "check_fabric_key.h"
#include "vtr_assert.h"
#include "vtr_log.h"
/* begin namespace openfpga */
namespace openfpga {
/** @brief Sanity checks for fabric key alias attribute:
* - Each alias should NOT be empty
* - Each alias should be defined only once!
*/
int check_fabric_key_alias(FabricKey& input_key, const bool& verbose) {
/* Check each key now */
size_t num_errors = 0;
float progress = 0.;
size_t num_keys_checked = 0;
std::map<std::string, size_t> alias_count;
for (FabricKeyId key_id : input_key.keys()) {
/* Note that this is slow. May consider to build a map first */
std::string curr_alias = input_key.key_alias(key_id);
progress = static_cast<float>(num_keys_checked) / static_cast<float>(input_key.num_keys()) * 100.0;
VTR_LOGV(verbose, "[%lu\%] Checking key alias '%s'\r", progress, curr_alias.c_str());
if (curr_alias.empty()) {
VTR_LOG_ERROR("Empty key alias (id='%lu') found in keys which is invalid!\n", size_t(key_id));
num_errors++;
}
auto result = alias_count.find(curr_alias);
if (result == alias_count.end()) {
alias_count[curr_alias] = 0;
} else {
alias_count[curr_alias] += 1;
}
num_keys_checked++;
}
for (const auto& kv : alias_count) {
if (kv.second > 1) {
std::string key_id_str;
std::vector<FabricKeyId> found_keys = input_key.find_key_by_alias(curr_alias);
for (FabricKeyId found_key_id : found_keys) {
key_id_str += std::to_string(size_t(found_key_id)) + ",";
}
found_keys.pop(); /* Remove last comma */
VTR_LOG_ERROR("Duplicated key alias '%s' found %lu times in keys (ids: %s), which is invalid!\n", kv.first.c_str(), kv.second, key_id_str.c_str());
num_errors++;
}
}
return num_errors;
}
/** @brief Sanity checks for fabric key name and value attribute:
* - Each name should not be empty
* - Each value should be larger than zero !
*/
int check_fabric_key_names_and_values(FabricKey& input_key, const bool& verbose) {
/* Check each key now */
size_t num_errors = 0;
float progress = 0.;
size_t num_keys_checked = 0;
std::map<std::string, std::map<size_t, size_t>> key_value_count;
for (FabricKeyId key_id : input_key.keys()) {
/* Note that this is slow. May consider to build a map first */
std::string curr_name = input_key.key_name(key_id);
size_t curr_value = input_key.key_value(key_id);
progress = static_cast<float>(num_keys_checked) / static_cast<float>(input_key.num_keys()) * 100.0;
VTR_LOGV(verbose, "[%lu\%] Checking key names and values '(%s, %lu)'\r", progress, curr_name.c_str(), curr_value);
if (curr_name.empty()) {
VTR_LOG_ERROR("Empty key name (id='%lu') found in keys which is invalid!\n", size_t(key_id));
num_errors++;
}
auto result = key_value_count[curr_name].find(curr_value);
if (result == key_value_count[curr_name].end()) {
key_value_count[curr_name][curr_value] = 0;
} else {
key_value_count[curr_name][curr_value] += 1;
}
num_keys_checked++;
}
for (const auto& key_name_kv : key_value_count) {
for (const auto& key_value_kv : key_name_kv.second) {
if (key_value_kv.second > 1) {
VTR_LOG_ERROR("Duplicated key name and value pair (%s, %lu) found %lu times in keys, which is invalid!\n", key_name_kv.first.c_str(), key_value_kv.first, key_value_kv.second);
num_errors++;
}
}
}
return num_errors;
}
} /* end namespace openfpga */

View File

@ -0,0 +1,22 @@
#ifndef CHECK_FABRIC_KEY_H
#define CHECK_FABRIC_KEY_H
/********************************************************************
* Include header files that are required by function declaration
*******************************************************************/
#include "fabric_key.h"
/********************************************************************
* Function declaration
*******************************************************************/
/* begin namespace openfpga */
namespace openfpga {
int check_fabric_key_alias(const FabricKey& input_key, const bool& verbose);
int check_fabric_key_names_values(const FabricKey& input_key, const bool& verbose);
} /* end namespace openfpga */
#endif

View File

@ -0,0 +1,169 @@
/********************************************************************
* Unit test functions to validate the correctness of
* 1. parser of data structures
* 2. writer of data structures
*******************************************************************/
/* Headers from vtrutils */
#include "vtr_assert.h"
#include "vtr_log.h"
/* Headers from fabric key */
#include "command_exit_codes.h"
#include "command_echo.h"
#include "command_parser.h"
#include "read_xml_fabric_key.h"
#include "write_xml_fabric_key.h"
#include "check_fabric_key.h"
/** @brief Initialize the options from command-line inputs and organize in the format that is ready for parsing */
static std::vector<std::string> format_argv(const std::string& cmd_name, int argc, const char** argv) {
std::vector<std::string> cmd_opts;
cmd_opts.push_back(cmd_name);
for (int iarg = 1; iarg < argc; ++iarg) {
cmd_opts.push_back(std::string(argv[iarg]));
}
return cmd_opts;
}
/** @brief Checks to be done:
* - Number of configuration regions match
* - Number of keys match
*/
static int check_input_key(FabricKey& input_key, const FabricKey& ref_key) {
if (ref_key.num_regions() != input_key.num_regions()) {
VTR_LOG_ERROR("Different number of configuration regions between reference key (='%lu') and input key ('=%lu')!\n", ref_key.num_regions(), input_key.num_regions());
return CMD_EXEC_FATAL_ERROR;
}
if (ref_key.num_keys() != input_key.num_keys()) {
VTR_LOG_ERROR("Different number of keys between reference key (='%lu') and input key ('=%lu')!\n", ref_key.num_keys(), input_key.num_keys());
return CMD_EXEC_FATAL_ERROR;
}
size_t num_errors = 0;
size_t curr_num_err = 0;
VTR_LOG("Checking key alias in reference key...\n");
curr_num_err = check_fabric_key_alias(ref_key, true);
VTR_LOG("Checking key alias in reference key... %s\n", curr_num_err ? "[Fail]" : "[Pass]");
VTR_LOG("Checking key names and values in reference key...\n");
curr_num_err = check_fabric_key_names_and_values(ref_key, true);
num_errors += curr_num_err;
VTR_LOG("Checking key names and valus in reference key... %s\n", curr_num_err ? "[Fail]" : "[Pass]");
VTR_LOG("Checking key alias in input key...\n");
curr_num_err = check_fabric_key_alias(input_key, true);
num_errors += curr_num_err;
VTR_LOG("Checking key alias in input key... %s\n", curr_num_err ? "[Fail]" : "[Pass]");
return num_errors ? CMD_EXEC_FATAL_ERROR : CMD_EXEC_SUCCESS;
}
/** @brief Checks to be done:
* - Each alias of reference key can be found in the input key
*/
static int check_input_and_ref_key_alias_match(FabricKey& input_key, const FabricKey& ref_key) {
size_t num_errors = 0;
size_t num_keys_checked = 0;
float progress = 0.;
VTR_LOG("Checking key alias matching between reference key and input keys...\n");
for (FabricKeyId key_id : ref_key.keys()) {
/* Note that this is slow. May consider to build a map first */
std::string curr_alias = ref_key.key_alias(key_id);
std::vector<FabricKeyId> input_found_keys = input_key.find_key_by_alias(curr_alias);
progress = static_cast<float>(num_keys_checked) / static_cast<float>(ref_key.num_keys()) * 100.0;
VTR_LOG("[%lu\%] Checking key alias '%s'\r", progress, curr_alias.c_str());
if (input_found_keys.empty()) {
VTR_LOG_ERROR("\nInvalid alias '%s' in the reference key (id='%lu'), which does not exist in the input key!\n", curr_alias.c_str(), size_t(key_id));
num_errors++;
}
if (input_found_keys.size() > 1) {
VTR_LOG_ERROR("\nInvalid alias '%s' in the input key (id='%lu'), which have been found %lu times!\n", curr_alias.c_str(), size_t(key_id));
num_errors++;
}
num_keys_checked++;
}
return num_errors ? CMD_EXEC_FATAL_ERROR : CMD_EXEC_SUCCESS;
}
/** @brief Checks to be done:
* - Each alias of input key can be found in the reference key
* - Update input key with pair of name and value which matches the alias from the reference key
*/
static int update_input_key(FabricKey& input_key, const FabricKey& ref_key) {
size_t num_errors = 0;
size_t num_keys_checked = 0;
float progress = 0.;
VTR_LOG("Pairing key alias between reference key and input keys...\n");
for (FabricKeyId key_id : input_key.keys()) {
/* Note that this is slow. May consider to build a map first */
std::string curr_alias = input_key.key_alias(key_id);
std::vector<FabricKeyId> ref_found_keys = ref_key.find_key_by_alias(curr_alias);
progress = static_cast<float>(num_keys_checked) / static_cast<float>(input_key.num_keys()) * 100.0;
VTR_LOG("[%lu\%] Pairing key alias '%s'\r", progress, curr_alias.c_str());
if (ref_found_keys.empty()) {
VTR_LOG_ERROR("\nInvalid alias '%s' in the input key (id='%lu'), which does not exist in the reference key!\n", curr_alias.c_str(), size_t(key_id));
num_errors++;
}
if (ref_found_keys.size() > 1) {
VTR_LOG_ERROR("\nInvalid alias '%s' in the reference key (id='%lu'), which have been found %lu times!\n", curr_alias.c_str(), size_t(key_id));
num_errors++;
}
/* Now we have a key, get the name and value, and update input key */
input_key.set_key_name(key_id, ref_key.key_name(ref_found_keys[0]));
input_key.set_key_value(key_id, ref_key.key_value(ref_found_keys[0]));
VTR_LOG("[%lu\%] Pairing key alias '%s' -> ('%s', %lu)\r", progress, curr_alias.c_str(), input_key.key_name(key_id).c_str(), input_key.key_value(key_id));
num_keys_checked++;
}
return num_errors ? CMD_EXEC_FATAL_ERROR : CMD_EXEC_SUCCESS;
}
/** @brief Checks to be done:
* - Number of configuration regions match
* - Number of keys match
* - Each alias can be found in the reference key
*/
static int check_and_update_input_key(FabricKey& input_key, const FabricKey& ref_key) {
int status = CMD_EXEC_SUCCESS;
status = check_input_key(input_key, ref_key);
if (status != CMD_EXEC_SUCCESS) {
return CMD_EXEC_FATAL_ERROR;
}
return update_input_key(input_key, ref_key);
}
int main(int argc, const char** argv) {
/* Create a new command and Initialize the options available in the user interface */
openfpga::Command cmd("fabric_key_assistant");
openfpga::CommandOptionId opt_ref = cmd.add_option("reference", true, "Specify the reference fabric key file");
cmd.set_option_require_value(opt_ref, OPT_STRING);
openfpga::CommandOptionId opt_input = cmd.add_option("input", true, "Specify the hand-crafted fabric key file");
cmd.set_option_require_value(opt_input, OPT_STRING);
openfpga::CommandOptionId opt_output = cmd.add_option("output", true, "Specify the final fabric key file to be outputted");
cmd.set_option_require_value(opt_output, OPT_STRING);
openfpga::CommandOptionId opt_help = cmd.add_option("help", true, "Show help desk");
/* Parse the option, to avoid issues, we use the command name to replace the
* argv[0] */
std::vector<std::string> cmd_opts = format_argv(cmd.name(), argc, argv);
CommandContext cmd_ctx(cmd);
if (false == parse_command(cmd_opts, cmd, cmd_ctx)) {
/* Echo the command */
print_command_options(cmd);
} else {
/* Let user to confirm selected options */
print_command_context(cmd, cmd_ctx);
}
/* Parse the fabric key from an XML file */
VTR_LOG("Read the reference fabric key from an XML file: %s.\n", cmd_ctx.option_value(cmd, opt_ref).c_str());
openfpga::FabricKey ref_key = openfpga::read_xml_fabric_key(cmd_ctx.option_value(cmd, opt_ref).c_str());
VTR_LOG("Read the hand-crafted fabric key from an XML file: %s.\n", cmd_ctx.option_value(cmd, opt_input).c_str());
openfpga::FabricKey input_key = openfpga::read_xml_fabric_key(cmd_ctx.option_value(cmd, opt_input).c_str());
/* Check the input key */
if (check_and_update_input_key(input_key, ref_key)) {
return openfpga::CMD_EXEC_FATAL_ERROR;
}
VTR_LOG("Write the final fabric key to an XML file: %s.\n", cmd_ctx.option_value(cmd, opt_output).c_str());
return openfpga::write_xml_fabric_key(cmd_ctx.option_value(cmd, opt_output).c_str(), input_key);
}