diff --git a/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst b/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst index 73024c458..20505ee82 100644 --- a/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst +++ b/docs/source/manual/openfpga_shell/openfpga_commands/setup_commands.rst @@ -127,15 +127,18 @@ write_gsb_to_xml - ``--exclude sb`` - ``--exclude [sb, cbx]`` - .. option:: --gsb_list + .. option:: --gsb_names Specify the name of GSB to be outputted. Users can specify multiple GSBs by using a splitter ``,``. When specified, only the GSBs whose names match the list will be outputted to files. If not specified, all the GSBs will be outputted. + + .. note:: When option ``--unique`` is enable, the given name of GSBs should match the unique modules! + For example, - - ``--gsb_list [gsb_2__4_, gsb_3__2_]`` - - ``--gsb_list gsb_2__4_`` + - ``--gsb_names [gsb_2__4_, gsb_3__2_]`` + - ``--gsb_names gsb_2__4_`` .. option:: --verbose diff --git a/openfpga/src/annotation/rr_gsb_writer_option.cpp b/openfpga/src/annotation/rr_gsb_writer_option.cpp new file mode 100644 index 000000000..0a12204b1 --- /dev/null +++ b/openfpga/src/annotation/rr_gsb_writer_option.cpp @@ -0,0 +1,120 @@ +/****************************************************************************** + * Memember functions for data structure RRGSBWriterOption + ******************************************************************************/ +#include "vtr_assert.h" +#include "vtr_log.h" + +#include "rr_gsb_writer_option.h" +#include "openfpga_tokenizer.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/************************************************** + * Public Constructors + *************************************************/ +RRGSBWriterOption::RRGSBWriterOption() { + output_directory_.clear(); + unique_module_only_ = false; + exclude_content_ = {false, false, false, false}; + gsb_names_.clear(); + verbose_output_ = false; + num_parse_errors_ = 0; +} + +/************************************************** + * Public Accessors + *************************************************/ +std::string RRGSBWriterOption::output_directory() const { + return output_directory_; +} + +bool RRGSBWriterOption::unique_module_only() const { + return unique_module_only_; +} + +bool RRGSBWriterOption::include_rr_info() const { + return !exclude_content_[0]; +} + +bool RRGSBWriterOption::include_cb_content(const t_rr_type& cb_type) const { + if (cb_type == CHANX) { + return !exclude_content_[1]; + } + VTR_ASSERT(cb_type == CHANY); + return !exclude_content_[2]; +} + +bool RRGSBWriterOption::include_sb_content() const { + return !exclude_content_[3]; +} + +std::vector RRGSBWriterOption::include_gsb_names() const { + return include_gsb_names_; +} + +bool RRGSBWriterOption::verbose_output() const { + return verbose_output_; +} + +/****************************************************************************** + * Private Mutators + ******************************************************************************/ +void RRGSBWriterOption::set_output_directory(const std::string& output_dir) { + output_directory_ = output_dir; +} + +void RRGSBWriterOption::set_unique_module_only(const bool& enabled) { + unique_module_only_ = enabled; +} + +void RRGSBWriterOption::set_exclude_rr_info(const bool& enabled) { + exclude_content_[0] = enabled; +} + +void RRGSBWriterOption::set_exclude_content(const std::string& content) { + num_parse_errors_ = 0; + /* Split the content using a tokenizer */ + StringToken tokenizer(content); + std::string tokens = tokenizer.split(','); + /* Parse each token */ + std::map token2index = { {"sb", 3}, {"cbx", 1}, {"cby", 2} }; + for (std::string token : tokens) { + auto result = token2index.find(token); + if (result == token2index.end()) { + /* Cannot find a valid keyword, error out */ + std::string keyword_list; + for (auto pair : token2index) { + keyword_list += pair.first + "|"; + } + keyword_list.pop_back(); + VTR_LOG_ERROR("Invalid content '" + token + "' to skip, expect [ " + keyword_list + " ]"); + num_parse_errors_++; + continue; + } + /* Now we should have a valid keyword, assign to designated flag */ + exclude_content_[result->second] = true; + } +} + +void RRGSBWriterOption::set_include_gsb_names(const std::string& gsb_names) { + /* Split the content using a tokenizer */ + StringToken tokenizer(content); + gsb_names_ = tokenizer.split(','); +} + +void RRGSBWriterOption::set_verbose_output(const bool& enabled) { + verbose_output_ = enabled; +} + +bool RRGSBWriterOption::valid() const { + if (output_directory_.empty()) { + return false; + } + if (num_parse_errors_) { + return false; + } + return true; +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/annotation/rr_gsb_writer_option.h b/openfpga/src/annotation/rr_gsb_writer_option.h new file mode 100644 index 000000000..2f997c4cb --- /dev/null +++ b/openfpga/src/annotation/rr_gsb_writer_option.h @@ -0,0 +1,65 @@ +#ifndef RR_GSB_WRITER_OPTION_H +#define RR_GSB_WRITER_OPTION_H + +/******************************************************************** + * Include header files required by the data structure definition + *******************************************************************/ +#include + +/* Begin namespace openfpga */ +namespace openfpga { + +/******************************************************************** + * Options for RRGSB writer + *******************************************************************/ +class RRGSBWriterOption { + public: /* Public constructor */ + /* Set default options */ + RRGSBWriterOption(); + public: /* Public accessors */ + std::string output_directory() const; + bool unique_module_only() const; + bool include_rr_info() const; + bool include_cb_content(const t_rr_type& cb_type) const; + bool include_sb_content() const; + std::vector include_gsb_names() const; + bool verbose_output() const; + public: /* Public mutators */ + void set_output_directory(const std::string& output_dir); + void set_unique_module_only(const bool& enabled); + void set_exclude_rr_info(const bool& enabled); + /* Parse the string which contains the content to be excluded + * Accepted string format is [sb|cbx|cby] + * Allow the use ',' as splitter + * For example: sb,cby + */ + void set_exclude_content(const std::string& content); + void set_include_gsb_names(const std::string& gsb_names); + void set_verbose_output(const bool& enabled); + public: /* Public validators */ + /* Check if the following internal data is valid or not: + * - output directory is assigned + * - no parsing errors + */ + bool valid() const; + private: /* Internal Data */ + std::string output_directory_; + bool unique_module_only_; + /* Flags to mark what content to be skipped when outputting: + * 0 : rr_info + * 1 : cbx + * 2 : cby + * 3 : sb + */ + std::array exclude_content_; + + std::vector gsb_names_; + bool verbose_output_; + + /* A flag to indicate if the data parse is invalid or not */ + int num_parse_errors_; +}; + +} /* End namespace openfpga*/ + +#endif diff --git a/openfpga/src/annotation/write_xml_device_rr_gsb.cpp b/openfpga/src/annotation/write_xml_device_rr_gsb.cpp index e778f7f49..7039647a0 100644 --- a/openfpga/src/annotation/write_xml_device_rr_gsb.cpp +++ b/openfpga/src/annotation/write_xml_device_rr_gsb.cpp @@ -182,7 +182,12 @@ void write_rr_switch_block_to_xml(const std::string fname_prefix, const VprDeviceAnnotation& vpr_device_annotation, const RRGraph& rr_graph, const RRGSB& rr_gsb, - const bool& verbose) { + const RRGSBWriterOption& options) { + + /* If there is a list of gsb list, we skip those which are not in the list */ + if (!include_gsb_names.empty() && include_gsb_names.end() != std::find(include_gsb_names.begin(), include_gsb_names.end(), curr_gsb_name() + write_rr_switch_block_to_xml(xml_dir_name, vpr_device_grid, vpr_device_annotation, rr_graph, rr_gsb, options); + /* Prepare file name */ std::string fname(fname_prefix); vtr::Point gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()); @@ -224,49 +229,133 @@ void write_rr_switch_block_to_xml(const std::string fname_prefix, fp.close(); } +/*************************************************************************************** + * Output internal structure (only the connection block part) of a RRGSB to XML format + ***************************************************************************************/ +static +void write_rr_connection_block_to_xml(const std::string fname_prefix, + const DeviceGrid& vpr_device_grid, + const VprDeviceAnnotation& vpr_device_annotation, + const RRGraph& rr_graph, + const RRGSB& rr_gsb, + const t_rr_type& cb_type, + const RRGSBWriterOption& options) { + + /* If there is a list of gsb list, we skip those which are not in the list */ + if (!include_gsb_names.empty() && include_gsb_names.end() != std::find(include_gsb_names.begin(), include_gsb_names.end(), curr_gsb_name() + write_rr_switch_block_to_xml(xml_dir_name, vpr_device_grid, vpr_device_annotation, rr_graph, rr_gsb, options); + + /* Prepare file name */ + std::string fname(fname_prefix); + vtr::Point cb_coordinate(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); + fname += generate_connection_block_module_name(cb_type, cb_coordinate); + fname += ".xml"; + + VTR_LOGV(verbose, + "Output internal structure of Connection Block to '%s'\n", + fname.c_str()); + + /* Create a file handler*/ + std::fstream fp; + /* Open a file */ + fp.open(fname, std::fstream::out | std::fstream::trunc); + + /* Validate the file stream */ + check_file_stream(fname.c_str(), fp); + + /* Output location of the Switch Block */ + fp << "" << std::endl; + + /* Output each side */ + for (size_t side = 0; side < rr_gsb.get_cb_ipin_sides(cb_type); ++side) { + SideManager gsb_side_manager(side); + enum e_side gsb_side = gsb_side_manager.get_side(); + + /* IPIN nodes and related connections */ + write_rr_gsb_ipin_connection_to_xml(fp, rr_graph, rr_gsb, gsb_side); + } + + fp << "" + << std::endl; + + /* close a file */ + fp.close(); +} + + /*************************************************************************************** * Output internal structure (only the switch block part) of all the RRGSBs * in a DeviceRRGSB to XML format ***************************************************************************************/ -void write_device_rr_gsb_to_xml(const char* sb_xml_dir, - const DeviceGrid& vpr_device_grid, +void write_device_rr_gsb_to_xml(const DeviceGrid& vpr_device_grid, const VprDeviceAnnotation& vpr_device_annotation, const RRGraph& rr_graph, const DeviceRRGSB& device_rr_gsb, - const bool& unique, - const bool& verbose) { - std::string xml_dir_name = format_dir_path(std::string(sb_xml_dir)); + const RRGSBWriterOption& options) { + std::string xml_dir_name = format_dir_path(options.output_directory); /* Create directories */ create_directory(xml_dir_name); vtr::Point sb_range = device_rr_gsb.get_gsb_range(); - size_t gsb_counter = 0; + size_t sb_counter = 0; + std::map cb_counters = 0; + std::map cb_names = { {CHANX, "X-direction"}, {CHANY, "Y-direction"} }; + + std::vector include_gsb_names = options.include_gsb_names(); /* For each switch block, an XML file will be outputted */ - if (unique) { + if (options.unique_module_only()) { /* Only output unique GSB modules */ VTR_LOG("Only output unique GSB modules to XML\n"); for (size_t igsb = 0; igsb < device_rr_gsb.get_num_gsb_unique_module(); ++igsb) { - const RRGSB& rr_gsb = device_rr_gsb.get_gsb_unique_module(igsb); - write_rr_switch_block_to_xml(xml_dir_name, vpr_device_grid, vpr_device_annotation, rr_graph, rr_gsb, verbose); - gsb_counter++; + const RRGSB& rr_gsb = device_rr_gsb.get_sb_unique_module(igsb); + /* Write CBx, CBy, SB on need */ + if (options.include_sb_content()) { + write_rr_switch_block_to_xml(xml_dir_name, vpr_device_grid, vpr_device_annotation, rr_graph, rr_gsb, options); + } + sb_counter++; + } + for (size_t igsb = 0; igsb < device_rr_gsb.get_num_gsb_unique_module(); ++igsb) { + const RRGSB& rr_gsb = device_rr_gsb.get_sb_unique_module(igsb); + for (t_rr_type cb_type : {CHANX, CHANY}) { + if (options.include_cb_content(cb_type)) { + write_rr_connection_block_to_xml(xml_dir_name, vpr_device_grid, vpr_device_annotation, rr_graph, rr_gsb, cb_type, options); + cb_counters[cb_type]++; + } + } } } else { /* Output all GSB instances in the fabric (some instances may share the same module) */ for (size_t ix = 0; ix < sb_range.x(); ++ix) { for (size_t iy = 0; iy < sb_range.y(); ++iy) { const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); - write_rr_switch_block_to_xml(xml_dir_name, vpr_device_grid, vpr_device_annotation, rr_graph, rr_gsb, verbose); - gsb_counter++; + /* Write CBx, CBy, SB on need */ + if (options.include_sb_content()) { + write_rr_switch_block_to_xml(xml_dir_name, vpr_device_grid, vpr_device_annotation, rr_graph, rr_gsb, options); + sb_counter++; + } + for (t_rr_type cb_type : {CHANX, CHANY}) { + if (options.include_cb_content(cb_type)) { + write_rr_connection_block_to_xml(xml_dir_name, vpr_device_grid, vpr_device_annotation, rr_graph, rr_gsb, cb_type, options); + cb_counters[cb_type]++; + } + } } } } - VTR_LOG("Output %lu XML files to directory '%s'\n", - gsb_counter, + VTR_LOG("Output %lu Switch blocks to XML files under directory '%s'\n", + sb_counter, xml_dir_name.c_str()); + for (t_rr_type cb_type : {CHANX, CHANY}) { + VTR_LOG("Output %lu %s Connection blocks to XML files under directory '%s'\n", + cb_counters[cb_type], + cb_names[cb_type].c_str(), + xml_dir_name.c_str()); + } } } /* end namespace openfpga */ diff --git a/openfpga/src/annotation/write_xml_device_rr_gsb.h b/openfpga/src/annotation/write_xml_device_rr_gsb.h index 06d647d7a..077e6ee2f 100644 --- a/openfpga/src/annotation/write_xml_device_rr_gsb.h +++ b/openfpga/src/annotation/write_xml_device_rr_gsb.h @@ -9,6 +9,7 @@ #include "device_grid.h" #include "vpr_device_annotation.h" #include "device_rr_gsb.h" +#include "rr_gsb_writer_option.h" /******************************************************************** * Function declaration @@ -17,13 +18,11 @@ /* begin namespace openfpga */ namespace openfpga { -void write_device_rr_gsb_to_xml(const char* sb_xml_dir, - const DeviceGrid& vpr_device_grid, +void write_device_rr_gsb_to_xml(const DeviceGrid& vpr_device_grid, const VprDeviceAnnotation& vpr_device_annotation, const RRGraph& rr_graph, const DeviceRRGSB& device_rr_gsb, - const bool& unique, - const bool& verbose); + const RRGSBWriterOption& options); } /* end namespace openfpga */ diff --git a/openfpga/src/base/openfpga_setup_command.cpp b/openfpga/src/base/openfpga_setup_command.cpp index b645a33ac..b0d24c231 100644 --- a/openfpga/src/base/openfpga_setup_command.cpp +++ b/openfpga/src/base/openfpga_setup_command.cpp @@ -213,6 +213,17 @@ ShellCommandId add_openfpga_write_gsb_command(openfpga::Shell& /* Add an option '--unique' */ shell_cmd.add_option("unique", false, "Only output unique GSB blocks"); + /* Add an option '--exclude_rr_info' */ + shell_cmd.add_option("exclude_rr_info", false, "Exclude routing resource graph information from output files, e.g., node id as well as other attributes. This is useful to check the connection inside GSBs purely."); + + /* Add an option '--exclude'*/ + CommandOptionId opt_exclude = shell_cmd.add_option("exclude", false, "Exclude part of the GSB data to be outputted. Can be [``sb``|``cbx``|``cby``]. Users can exclude multiple parts by using a splitter ``,``"); + shell_cmd.set_option_require_value(opt_exclude, openfpga::OPT_STRING); + + /* Add an option '--gsb_names'*/ + CommandOptionId opt_gsb_names = shell_cmd.add_option("gsb_names", false, "Specify the name of GSB to be outputted. Users can specify multiple GSBs by using a splitter ``,``"); + shell_cmd.set_option_require_value(opt_gsb_names, openfpga::OPT_STRING); + /* Add an option '--verbose' */ shell_cmd.add_option("verbose", false, "Show verbose outputs"); diff --git a/openfpga/src/base/openfpga_write_gsb.cpp b/openfpga/src/base/openfpga_write_gsb.cpp index cab50bd3d..6ec4a19e8 100644 --- a/openfpga/src/base/openfpga_write_gsb.cpp +++ b/openfpga/src/base/openfpga_write_gsb.cpp @@ -34,17 +34,25 @@ int write_gsb(const OpenfpgaContext& openfpga_ctx, VTR_ASSERT(false == cmd_context.option_value(cmd, opt_file).empty()); CommandOptionId opt_unique = cmd.option("unique"); + CommandOptionId opt_exclude_rr_info = cmd.option("exclude_rr_info"); + CommandOptionId opt_exclude = cmd.option("exclude"); + CommandOptionId opt_gsb_names = cmd.option("gsb_names"); CommandOptionId opt_verbose = cmd.option("verbose"); - std::string sb_file_name = cmd_context.option_value(cmd, opt_file); + /* Build the options for the writer */ + RRGSBWriterOption options; + options.set_output_directory(cmd_context.option_value(cmd, opt_file)); + options.set_unique_module_only(cmd_context.option_enable(cmd, opt_unique)); + options.set_exclude_rr_info(cmd_context.option_enable(cmd, opt_exclude_rr_info)); + options.set_exclude_content(cmd_context.option_value(cmd, opt_exclude)); + options.set_include_gsb_list(cmd_context.option_value(cmd, opt_gsb_names)); + options.set_verbose_output(cmd_context.option_enable(cmd, opt_verbose)); - write_device_rr_gsb_to_xml(sb_file_name.c_str(), - g_vpr_ctx.device().grid, + write_device_rr_gsb_to_xml(g_vpr_ctx.device().grid, openfpga_ctx.vpr_device_annotation(), g_vpr_ctx.device().rr_graph, openfpga_ctx.device_rr_gsb(), - cmd_context.option_enable(cmd, opt_unique), - cmd_context.option_enable(cmd, opt_verbose)); + options); /* TODO: should identify the error code from internal function execution */ return CMD_EXEC_SUCCESS;