Merge pull request #1321 from lnis-uofu/xt_fabric_key_assistant
Add a utility tool: fabric key assistant
This commit is contained in:
commit
aef4e76afe
|
@ -18,3 +18,5 @@
|
||||||
|
|
||||||
file_formats/index
|
file_formats/index
|
||||||
|
|
||||||
|
utilities/index
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
.. _utility_fabric_key_assistant:
|
||||||
|
|
||||||
|
Fabric Key Assistant
|
||||||
|
--------------------
|
||||||
|
|
||||||
|
Fabric Key Assistant is a tool to help users to craft fabric key files (see details in :ref:`file_formats_fabric_key`).
|
||||||
|
Note that crafting a fabric key is not an easy task for engineers, as its complexity grows exponentially with FPGA sizes.
|
||||||
|
This tool is developed to assist engineers when finalizing fabric key files.
|
||||||
|
It can apply sanity checks on hand-crafted fabric key files, helping engineers to correct and debug.
|
||||||
|
|
||||||
|
The tool can be found at ``/build/libs/libfabrickey/test/fabric_key_assistant``
|
||||||
|
|
||||||
|
The tool includes the following options:
|
||||||
|
|
||||||
|
.. option:: --reference <string>
|
||||||
|
|
||||||
|
Specifiy a reference fabric key file, which has been already validated by OpenFPGA. For example, the reference fabric key can be a file which is written by OpenFPGA as a default key. The reference fabric key file is treated as the baseline, on which the input fabric key file will be compared to.
|
||||||
|
|
||||||
|
.. note:: The reference fabric key should contain all the syntax, e.g., ``name``, ``value`` and ``alias``.
|
||||||
|
|
||||||
|
.. option:: --input <string>
|
||||||
|
|
||||||
|
Specify the input fabric key file, which is typically hand-crafted by users. Sanity checks will be applied to the input fabric key file by comparing the reference.
|
||||||
|
|
||||||
|
.. note:: The input fabric key should contain only the syntax ``alias``.
|
||||||
|
|
||||||
|
.. option:: --output <string>
|
||||||
|
|
||||||
|
Specify the output fabric key file, which is an updated version of the input fabric key file. Difference from the input file, the output file contains ``name`` and ``value``, which is added by linking the ``alias`` from input file to reference file. For example, the reference fabric key includes a key:
|
||||||
|
|
||||||
|
.. code-block:: xml
|
||||||
|
|
||||||
|
<key id="1" name="tile_0__0_" value="5" alias="tile_4__2_"/>
|
||||||
|
|
||||||
|
while the input fabric key includes a key:
|
||||||
|
|
||||||
|
.. code-block:: xml
|
||||||
|
|
||||||
|
<key id="23" alias="tile_4__2_"/>
|
||||||
|
|
||||||
|
the resulting output fabric key file includes a key:
|
||||||
|
|
||||||
|
.. code-block:: xml
|
||||||
|
|
||||||
|
<key id="23" name="tile_0__0_" value="5" alias="tile_4__2_"/>
|
||||||
|
|
||||||
|
.. option:: --verbose
|
||||||
|
|
||||||
|
To enable verbose output
|
||||||
|
|
||||||
|
.. option:: --help
|
||||||
|
|
||||||
|
Show help desk
|
|
@ -0,0 +1,13 @@
|
||||||
|
Utilities
|
||||||
|
---------
|
||||||
|
|
||||||
|
OpenFPGA contains a number of utility tools to help users to craft files.
|
||||||
|
|
||||||
|
|
||||||
|
.. _utilities:
|
||||||
|
Utility Tools
|
||||||
|
|
||||||
|
.. toctree::
|
||||||
|
:maxdepth: 2
|
||||||
|
|
||||||
|
fabric_key_assistant
|
|
@ -20,6 +20,7 @@ set_target_properties(libfabrickey PROPERTIES PREFIX "") #Avoid extra 'lib' pref
|
||||||
#Specify link-time dependancies
|
#Specify link-time dependancies
|
||||||
target_link_libraries(libfabrickey
|
target_link_libraries(libfabrickey
|
||||||
libopenfpgautil
|
libopenfpgautil
|
||||||
|
libopenfpgashell
|
||||||
libarchopenfpga
|
libarchopenfpga
|
||||||
libvtrutil
|
libvtrutil
|
||||||
libpugiutil)
|
libpugiutil)
|
||||||
|
|
|
@ -55,6 +55,35 @@ std::vector<FabricSubKeyId> FabricKey::sub_keys(
|
||||||
/************************************************************************
|
/************************************************************************
|
||||||
* Public Accessors : Basic data query
|
* 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> FabricKey::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(
|
std::vector<FabricKeyId> FabricKey::region_keys(
|
||||||
const FabricRegionId& region_id) const {
|
const FabricRegionId& region_id) const {
|
||||||
/* validate the region_id */
|
/* validate the region_id */
|
||||||
|
|
|
@ -75,6 +75,8 @@ class FabricKey {
|
||||||
const FabricKeyModuleId& module_id) const;
|
const FabricKeyModuleId& module_id) const;
|
||||||
|
|
||||||
public: /* Public Accessors: Basic data query */
|
public: /* Public Accessors: Basic data query */
|
||||||
|
size_t num_regions() const;
|
||||||
|
size_t num_keys() const;
|
||||||
/* Access all the keys of a region */
|
/* Access all the keys of a region */
|
||||||
std::vector<FabricKeyId> region_keys(const FabricRegionId& region_id) const;
|
std::vector<FabricKeyId> region_keys(const FabricRegionId& region_id) const;
|
||||||
/* Access the name of a key */
|
/* Access the name of a key */
|
||||||
|
@ -86,6 +88,12 @@ class FabricKey {
|
||||||
/* Access the coordinate of a key */
|
/* Access the coordinate of a key */
|
||||||
vtr::Point<int> key_coordinate(const FabricKeyId& key_id) const;
|
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 */
|
/* Check if there are any keys */
|
||||||
bool empty() const;
|
bool empty() const;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
/************************************************************************
|
||||||
|
* 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(const 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", size_t(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(kv.first);
|
||||||
|
for (FabricKeyId found_key_id : found_keys) {
|
||||||
|
key_id_str += std::to_string(size_t(found_key_id)) + ",";
|
||||||
|
}
|
||||||
|
key_id_str.pop_back(); /* 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(const 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",
|
||||||
|
size_t(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 */
|
|
@ -0,0 +1,23 @@
|
||||||
|
#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_and_values(const FabricKey& input_key,
|
||||||
|
const bool& verbose);
|
||||||
|
|
||||||
|
} /* end namespace openfpga */
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,233 @@
|
||||||
|
/********************************************************************
|
||||||
|
* 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 "check_fabric_key.h"
|
||||||
|
#include "command_echo.h"
|
||||||
|
#include "command_exit_codes.h"
|
||||||
|
#include "command_parser.h"
|
||||||
|
#include "read_xml_fabric_key.h"
|
||||||
|
#include "write_xml_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:
|
||||||
|
* - Each alias of reference key can be found in the input key
|
||||||
|
*/
|
||||||
|
static int check_input_and_ref_key_alias_match(
|
||||||
|
const openfpga::FabricKey& input_key, const openfpga::FabricKey& ref_key,
|
||||||
|
const bool& verbose) {
|
||||||
|
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 (openfpga::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<openfpga::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_LOGV(verbose, "[%lu%] Checking key alias '%s'\r", size_t(progress),
|
||||||
|
curr_alias.c_str());
|
||||||
|
if (input_found_keys.empty()) {
|
||||||
|
VTR_LOG_ERROR(
|
||||||
|
"Invalid 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(
|
||||||
|
"Invalid alias '%s' in the input key (id='%lu'), which have been "
|
||||||
|
"found %lu times!\n",
|
||||||
|
curr_alias.c_str(), size_t(key_id), input_found_keys.size());
|
||||||
|
num_errors++;
|
||||||
|
}
|
||||||
|
num_keys_checked++;
|
||||||
|
}
|
||||||
|
VTR_LOG(
|
||||||
|
"Checking key alias matching between reference key and input keys... %s\n",
|
||||||
|
num_errors ? "[Fail]" : "[Pass]");
|
||||||
|
return num_errors ? openfpga::CMD_EXEC_FATAL_ERROR
|
||||||
|
: openfpga::CMD_EXEC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @brief Checks to be done:
|
||||||
|
* - Number of configuration regions match
|
||||||
|
* - Number of keys match
|
||||||
|
*/
|
||||||
|
static int check_input_key(const openfpga::FabricKey& input_key,
|
||||||
|
const openfpga::FabricKey& ref_key,
|
||||||
|
const bool& verbose) {
|
||||||
|
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 openfpga::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 openfpga::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 = openfpga::check_fabric_key_alias(ref_key, verbose);
|
||||||
|
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 = openfpga::check_fabric_key_names_and_values(ref_key, verbose);
|
||||||
|
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 = openfpga::check_fabric_key_alias(input_key, verbose);
|
||||||
|
num_errors += curr_num_err;
|
||||||
|
VTR_LOG("Checking key alias in input key... %s\n",
|
||||||
|
curr_num_err ? "[Fail]" : "[Pass]");
|
||||||
|
num_errors +=
|
||||||
|
check_input_and_ref_key_alias_match(input_key, ref_key, verbose);
|
||||||
|
return num_errors ? openfpga::CMD_EXEC_FATAL_ERROR
|
||||||
|
: openfpga::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(openfpga::FabricKey& input_key,
|
||||||
|
const openfpga::FabricKey& ref_key,
|
||||||
|
const bool& verbose) {
|
||||||
|
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 (openfpga::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<openfpga::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_LOGV(verbose, "[%lu%] Pairing key alias '%s'\r", size_t(progress),
|
||||||
|
curr_alias.c_str());
|
||||||
|
if (ref_found_keys.empty()) {
|
||||||
|
VTR_LOG_ERROR(
|
||||||
|
"Invalid 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(
|
||||||
|
"Invalid 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_LOGV(verbose, "[%lu%] Pairing key alias '%s' -> ('%s', %lu)\r",
|
||||||
|
size_t(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 ? openfpga::CMD_EXEC_FATAL_ERROR
|
||||||
|
: openfpga::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(openfpga::FabricKey& input_key,
|
||||||
|
const openfpga::FabricKey& ref_key,
|
||||||
|
const bool& verbose) {
|
||||||
|
int status = openfpga::CMD_EXEC_SUCCESS;
|
||||||
|
status = check_input_key(input_key, ref_key, verbose);
|
||||||
|
if (status != openfpga::CMD_EXEC_SUCCESS) {
|
||||||
|
return openfpga::CMD_EXEC_FATAL_ERROR;
|
||||||
|
}
|
||||||
|
return update_input_key(input_key, ref_key, verbose);
|
||||||
|
}
|
||||||
|
|
||||||
|
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, openfpga::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, openfpga::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, openfpga::OPT_STRING);
|
||||||
|
openfpga::CommandOptionId opt_verbose =
|
||||||
|
cmd.add_option("verbose", false, "Show verbose outputs");
|
||||||
|
openfpga::CommandOptionId opt_help =
|
||||||
|
cmd.add_option("help", false, "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);
|
||||||
|
|
||||||
|
openfpga::CommandContext cmd_ctx(cmd);
|
||||||
|
if (false == parse_command(cmd_opts, cmd, cmd_ctx) ||
|
||||||
|
cmd_ctx.option_enable(cmd, opt_help)) {
|
||||||
|
/* Echo the command */
|
||||||
|
print_command_options(cmd);
|
||||||
|
return openfpga::CMD_EXEC_FATAL_ERROR;
|
||||||
|
} 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,
|
||||||
|
cmd_ctx.option_enable(cmd, opt_verbose))) {
|
||||||
|
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);
|
||||||
|
}
|
Loading…
Reference in New Issue