diff --git a/docs/source/manual/file_formats/index.rst b/docs/source/manual/file_formats/index.rst
index f02218cd3..d1a77e2d0 100644
--- a/docs/source/manual/file_formats/index.rst
+++ b/docs/source/manual/file_formats/index.rst
@@ -21,3 +21,5 @@ OpenFPGA widely uses XML format for interchangable files
bitstream_setting
fabric_key
+
+ io_mapping_file
diff --git a/docs/source/manual/file_formats/io_mapping_file.rst b/docs/source/manual/file_formats/io_mapping_file.rst
new file mode 100644
index 000000000..722f477f6
--- /dev/null
+++ b/docs/source/manual/file_formats/io_mapping_file.rst
@@ -0,0 +1,33 @@
+.. _file_format_io_mapping_file:
+
+I/O Mapping File (.xml)
+-----------------------
+
+The I/O mapping file aims to show
+
+- What nets have been mapped to each I/O
+- What is the directionality of each mapped I/O
+
+An example of design constraints is shown as follows.
+
+.. code-block:: xml
+
+
+
+
+
+
+
+.. option:: name=""
+
+ The pin name of the FPGA fabric which has been mapped, which should be a valid pin defined in OpenFPGA architecture description.
+
+ .. note:: You should be find the exact pin in the top-level module of FPGA fabric if you output the Verilog netlists.
+
+.. option:: net=""
+
+ The net name which is actually mapped to a pin, which should be consistent with net definition in your ``.blif`` file.
+
+.. option:: dir=""
+
+ The direction of an I/O, which can be either ``input`` or ``output``.
diff --git a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_bitstream_commands.rst b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_bitstream_commands.rst
index d2461b5ef..fbe10e033 100644
--- a/docs/source/manual/openfpga_shell/openfpga_commands/fpga_bitstream_commands.rst
+++ b/docs/source/manual/openfpga_shell/openfpga_commands/fpga_bitstream_commands.rst
@@ -72,3 +72,19 @@ write_fabric_bitstream
.. option:: --verbose
Show verbose log
+
+write_io_mapping
+~~~~~~~~~~~~~~~~
+
+ Output the I/O mapping information to a file
+
+ .. option:: --file or -f
+
+ Specify the file name where the I/O mapping will be outputted to.
+ See file formats in :ref:`file_format_io_mapping_file`.
+
+ .. option:: --verbose
+
+ Show verbose log
+
+
diff --git a/openfpga/src/base/io_map.cpp b/openfpga/src/base/io_map.cpp
new file mode 100644
index 000000000..3012c4285
--- /dev/null
+++ b/openfpga/src/base/io_map.cpp
@@ -0,0 +1,54 @@
+/******************************************************************************
+ * Memember functions for data structure IoMap
+ ******************************************************************************/
+#include "vtr_assert.h"
+
+#include "io_map.h"
+
+/* begin namespace openfpga */
+namespace openfpga {
+
+IoMap::io_map_range IoMap::io_map() const {
+ return vtr::make_range(io_map_ids_.begin(), io_map_ids_.end());
+}
+
+BasicPort IoMap::io_port(IoMapId io_map_id) const {
+ VTR_ASSERT(valid_io_map_id(io_map_id));
+ return io_ports_[io_map_id];
+}
+
+BasicPort IoMap::io_net(IoMapId io_map_id) const {
+ VTR_ASSERT(valid_io_map_id(io_map_id));
+ return mapped_nets_[io_map_id];
+}
+
+bool IoMap::is_io_output(IoMapId io_map_id) const {
+ VTR_ASSERT(valid_io_map_id(io_map_id));
+ return IoMap::IO_MAP_DIR_OUTPUT == io_directionality_[io_map_id];
+}
+
+bool IoMap::is_io_input(IoMapId io_map_id) const {
+ VTR_ASSERT(valid_io_map_id(io_map_id));
+ return IoMap::IO_MAP_DIR_INPUT == io_directionality_[io_map_id];
+}
+
+IoMapId IoMap::create_io_mapping(const BasicPort& port,
+ const BasicPort& net,
+ IoMap::e_direction dir) {
+ /* Create a new id */
+ IoMapId io_map_id = IoMapId(io_map_ids_.size());
+ io_map_ids_.push_back(io_map_id);
+
+ /* Allocate related attributes */
+ io_ports_.push_back(port);
+ mapped_nets_.push_back(net);
+ io_directionality_.push_back(dir);
+
+ return io_map_id;
+}
+
+bool IoMap::valid_io_map_id(IoMapId io_map_id) const {
+ return (size_t(io_map_id) < io_map_ids_.size()) && (io_map_id == io_map_ids_[io_map_id]);
+}
+
+} /* end namespace openfpga */
diff --git a/openfpga/src/base/io_map.h b/openfpga/src/base/io_map.h
new file mode 100644
index 000000000..5bc378000
--- /dev/null
+++ b/openfpga/src/base/io_map.h
@@ -0,0 +1,59 @@
+#ifndef IO_MAP_H
+#define IO_MAP_H
+
+/********************************************************************
+ * Include header files required by the data structure definition
+ *******************************************************************/
+#include "vtr_vector.h"
+#include "openfpga_port.h"
+#include "io_map_fwd.h"
+
+/* Begin namespace openfpga */
+namespace openfpga {
+
+/********************************************************************
+ * This is a data structure storing io mapping information
+ * - the net-to-I/O mapping
+ * - each I/O directionality
+ *******************************************************************/
+class IoMap {
+ public: /* Types and ranges */
+ enum e_direction {
+ IO_MAP_DIR_INPUT,
+ IO_MAP_DIR_OUTPUT,
+ NUM_IO_MAP_DIR_TYPES
+ };
+ typedef vtr::vector::const_iterator io_map_iterator;
+ typedef vtr::Range io_map_range;
+ public: /* Public aggregators */
+ /* Find all io mapping */
+ io_map_range io_map() const;
+
+ /* Get the port of the io that is mapped */
+ BasicPort io_port(IoMapId io_map_id) const;
+
+ /* Get the net of the io that is mapped to */
+ BasicPort io_net(IoMapId io_map_id) const;
+
+ /* Query on if an io is configured as an input */
+ bool is_io_input(IoMapId io_map_id) const;
+
+ /* Query on if an io is configured as an output */
+ bool is_io_output(IoMapId io_map_id) const;
+ public: /* Public mutators */
+ /* Create a new I/O mapping */
+ IoMapId create_io_mapping(const BasicPort& port,
+ const BasicPort& net,
+ e_direction dir);
+ public: /* Public validators/invalidators */
+ bool valid_io_map_id(IoMapId io_map_id) const;
+ private: /* Internal Data */
+ vtr::vector io_map_ids_;
+ vtr::vector io_ports_;
+ vtr::vector mapped_nets_;
+ vtr::vector io_directionality_;
+};
+
+} /* End namespace openfpga*/
+
+#endif
diff --git a/openfpga/src/base/io_map_fwd.h b/openfpga/src/base/io_map_fwd.h
new file mode 100644
index 000000000..0de3e99c1
--- /dev/null
+++ b/openfpga/src/base/io_map_fwd.h
@@ -0,0 +1,23 @@
+/**************************************************
+ * This file includes only declarations for
+ * the data structures for IoMap
+ * Please refer to io_map.h for more details
+ *************************************************/
+#ifndef IO_MAP_FWD_H
+#define IO_MAP_FWD_H
+
+#include "vtr_strong_id.h"
+
+/* begin namespace openfpga */
+namespace openfpga {
+
+/* Strong Ids */
+struct io_map_id_tag;
+
+typedef vtr::StrongId IoMapId;
+
+class IoMap;
+
+} /* end namespace openfpga */
+
+#endif
diff --git a/openfpga/src/base/openfpga_bitstream.cpp b/openfpga/src/base/openfpga_bitstream.cpp
index c32e9ae87..c35017ba8 100644
--- a/openfpga/src/base/openfpga_bitstream.cpp
+++ b/openfpga/src/base/openfpga_bitstream.cpp
@@ -15,10 +15,14 @@
#include "read_xml_arch_bitstream.h"
#include "write_xml_arch_bitstream.h"
+#include "openfpga_naming.h"
+
#include "build_device_bitstream.h"
#include "write_text_fabric_bitstream.h"
#include "write_xml_fabric_bitstream.h"
#include "build_fabric_bitstream.h"
+#include "build_io_mapping_info.h"
+#include "write_xml_io_mapping.h"
#include "openfpga_bitstream.h"
/* Include global variables of VPR */
@@ -121,4 +125,44 @@ int write_fabric_bitstream(const OpenfpgaContext& openfpga_ctx,
return status;
}
+/********************************************************************
+ * A wrapper function to call the write_io_mapping() in FPGA bitstream
+ *******************************************************************/
+int write_io_mapping(const OpenfpgaContext& openfpga_ctx,
+ const Command& cmd, const CommandContext& cmd_context) {
+
+ CommandOptionId opt_verbose = cmd.option("verbose");
+ CommandOptionId opt_file = cmd.option("file");
+
+ /* Write fabric bitstream if required */
+ int status = CMD_EXEC_SUCCESS;
+
+ VTR_ASSERT(true == cmd_context.option_enable(cmd, opt_file));
+
+ std::string src_dir_path = find_path_dir_name(cmd_context.option_value(cmd, opt_file));
+
+ /* Create directories */
+ create_directory(src_dir_path);
+
+ /* Create a module as the top-level fabric, and add it to the module manager */
+ std::string top_module_name = generate_fpga_top_module_name();
+ ModuleId top_module = openfpga_ctx.module_graph().find_module(top_module_name);
+ VTR_ASSERT(true == openfpga_ctx.module_graph().valid_module_id(top_module));
+
+ IoMap io_map = build_fpga_io_mapping_info(openfpga_ctx.module_graph(),
+ top_module,
+ g_vpr_ctx.atom(),
+ g_vpr_ctx.placement(),
+ openfpga_ctx.io_location_map(),
+ openfpga_ctx.vpr_netlist_annotation(),
+ std::string(),
+ std::string());
+
+ status = write_io_mapping_to_xml_file(io_map,
+ cmd_context.option_value(cmd, opt_file),
+ cmd_context.option_enable(cmd, opt_verbose));
+
+ return status;
+}
+
} /* end namespace openfpga */
diff --git a/openfpga/src/base/openfpga_bitstream.h b/openfpga/src/base/openfpga_bitstream.h
index c210d6bab..2e438d56b 100644
--- a/openfpga/src/base/openfpga_bitstream.h
+++ b/openfpga/src/base/openfpga_bitstream.h
@@ -24,6 +24,9 @@ int build_fabric_bitstream(OpenfpgaContext& openfpga_ctx,
int write_fabric_bitstream(const OpenfpgaContext& openfpga_ctx,
const Command& cmd, const CommandContext& cmd_context);
+int write_io_mapping(const OpenfpgaContext& openfpga_ctx,
+ const Command& cmd, const CommandContext& cmd_context);
+
} /* end namespace openfpga */
#endif
diff --git a/openfpga/src/base/openfpga_bitstream_command.cpp b/openfpga/src/base/openfpga_bitstream_command.cpp
index 6b7397a1c..3599bcac5 100644
--- a/openfpga/src/base/openfpga_bitstream_command.cpp
+++ b/openfpga/src/base/openfpga_bitstream_command.cpp
@@ -131,6 +131,36 @@ ShellCommandId add_openfpga_write_fabric_bitstream_command(openfpga::Shell& shell,
+ const ShellCommandClassId& cmd_class_id,
+ const std::vector& dependent_cmds) {
+ Command shell_cmd("write_io_mapping");
+
+ /* Add an option '--file' in short '-f'*/
+ CommandOptionId opt_file = shell_cmd.add_option("file", true, "file path to output the io mapping information");
+ shell_cmd.set_option_short_name(opt_file, "f");
+ shell_cmd.set_option_require_value(opt_file, openfpga::OPT_STRING);
+
+ /* Add an option '--verbose' */
+ shell_cmd.add_option("verbose", false, "Enable verbose output");
+
+ /* Add command 'fabric_bitstream' to the Shell */
+ ShellCommandId shell_cmd_id = shell.add_command(shell_cmd, "Write the I/O mapping information to a file");
+ shell.set_command_class(shell_cmd_id, cmd_class_id);
+ shell.set_command_execute_function(shell_cmd_id, write_io_mapping);
+
+ /* Add command dependency to the Shell */
+ shell.set_command_dependency(shell_cmd_id, dependent_cmds);
+
+ return shell_cmd_id;
+}
+
/********************************************************************
* Top-level function to add all the commands related to FPGA-Bitstream
*******************************************************************/
@@ -172,6 +202,14 @@ void add_openfpga_bitstream_commands(openfpga::Shell& shell) {
std::vector cmd_dependency_write_fabric_bitstream;
cmd_dependency_write_fabric_bitstream.push_back(shell_cmd_build_fabric_bitstream_id);
add_openfpga_write_fabric_bitstream_command(shell, openfpga_bitstream_cmd_class, cmd_dependency_write_fabric_bitstream);
+
+ /********************************
+ * Command 'write_io_mapping'
+ */
+ /* The 'write_io_mapping' command should NOT be executed before 'build_fabric' */
+ std::vector cmd_dependency_write_io_mapping;
+ cmd_dependency_write_io_mapping.push_back(shell_cmd_build_fabric_id);
+ add_openfpga_write_io_mapping_command(shell, openfpga_bitstream_cmd_class, cmd_dependency_write_io_mapping);
}
} /* end namespace openfpga */
diff --git a/openfpga/src/fpga_bitstream/build_io_mapping_info.cpp b/openfpga/src/fpga_bitstream/build_io_mapping_info.cpp
new file mode 100644
index 000000000..76fbda9a2
--- /dev/null
+++ b/openfpga/src/fpga_bitstream/build_io_mapping_info.cpp
@@ -0,0 +1,144 @@
+/********************************************************************
+ * This file includes functions that build io mapping information
+ *******************************************************************/
+#include
+#include
+#include
+
+/* Headers from vtrutil library */
+#include "vtr_assert.h"
+#include "vtr_log.h"
+#include "vtr_time.h"
+
+/* Headers from archopenfpga library */
+#include "openfpga_naming.h"
+
+#include "module_manager_utils.h"
+#include "build_io_mapping_info.h"
+
+/* begin namespace openfpga */
+namespace openfpga {
+
+/********************************************************************
+ * This function
+ * - builds the net-to-I/O mapping
+ * - identifies each I/O directionality
+ * - return a database containing the above information
+ *
+ * TODO: This function duplicates codes from
+ * function: print_verilog_testbench_connect_fpga_ios() in
+ * source file: verilog_testbench_utils.cpp
+ * Should consider how to merge the codes and share same builder function
+ *******************************************************************/
+IoMap build_fpga_io_mapping_info(const ModuleManager& module_manager,
+ const ModuleId& top_module,
+ const AtomContext& atom_ctx,
+ const PlacementContext& place_ctx,
+ const IoLocationMap& io_location_map,
+ const VprNetlistAnnotation& netlist_annotation,
+ const std::string& io_input_port_name_postfix,
+ const std::string& io_output_port_name_postfix) {
+ IoMap io_map;
+
+ /* Only mappable i/o ports can be considered */
+ std::vector module_io_ports;
+ for (const ModuleManager::e_module_port_type& module_io_port_type : MODULE_IO_PORT_TYPES) {
+ for (const ModulePortId& gpio_port_id : module_manager.module_port_ids_by_type(top_module, module_io_port_type)) {
+ /* Only care mappable I/O */
+ if (false == module_manager.port_is_mappable_io(top_module, gpio_port_id)) {
+ continue;
+ }
+ module_io_ports.push_back(gpio_port_id);
+ }
+ }
+
+ /* Type mapping between VPR block and Module port */
+ std::map atom_block_type_to_module_port_type;
+ atom_block_type_to_module_port_type[AtomBlockType::INPAD] = ModuleManager::MODULE_GPIN_PORT;
+ atom_block_type_to_module_port_type[AtomBlockType::OUTPAD] = ModuleManager::MODULE_GPOUT_PORT;
+
+ /* Type mapping between VPR block and io mapping direction */
+ std::map atom_block_type_to_io_map_direction;
+ atom_block_type_to_io_map_direction[AtomBlockType::INPAD] = IoMap::IO_MAP_DIR_INPUT;
+ atom_block_type_to_io_map_direction[AtomBlockType::OUTPAD] = IoMap::IO_MAP_DIR_OUTPUT;
+
+ for (const AtomBlockId& atom_blk : atom_ctx.nlist.blocks()) {
+ /* Bypass non-I/O atom blocks ! */
+ if ( (AtomBlockType::INPAD != atom_ctx.nlist.block_type(atom_blk))
+ && (AtomBlockType::OUTPAD != atom_ctx.nlist.block_type(atom_blk)) ) {
+ continue;
+ }
+
+ /* If there is a GPIO port, use it directly
+ * Otherwise, should find a GPIN for INPAD
+ * or should find a GPOUT for OUTPAD
+ */
+ std::pair mapped_module_io_info = std::make_pair(ModulePortId::INVALID(), -1);
+ for (const ModulePortId& module_io_port_id : module_io_ports) {
+ const BasicPort& module_io_port = module_manager.module_port(top_module, module_io_port_id);
+
+ /* Find the index of the mapped GPIO in top-level FPGA fabric */
+ size_t temp_io_index = io_location_map.io_index(place_ctx.block_locs[atom_ctx.lookup.atom_clb(atom_blk)].loc.x,
+ place_ctx.block_locs[atom_ctx.lookup.atom_clb(atom_blk)].loc.y,
+ place_ctx.block_locs[atom_ctx.lookup.atom_clb(atom_blk)].loc.z,
+ module_io_port.get_name());
+
+ /* Bypass invalid index (not mapped to this GPIO port) */
+ if (size_t(-1) == temp_io_index) {
+ continue;
+ }
+
+ /* If the port is an GPIO port, just use it */
+ if (ModuleManager::MODULE_GPIO_PORT == module_manager.port_type(top_module, module_io_port_id)) {
+ mapped_module_io_info = std::make_pair(module_io_port_id, temp_io_index);
+ break;
+ }
+
+ /* If this is an INPAD, we can use an GPIN port (if available) */
+ if (atom_block_type_to_module_port_type[atom_ctx.nlist.block_type(atom_blk)] == module_manager.port_type(top_module, module_io_port_id)) {
+ mapped_module_io_info = std::make_pair(module_io_port_id, temp_io_index);
+ break;
+ }
+ }
+
+ /* We must find a valid one */
+ VTR_ASSERT(true == module_manager.valid_module_port_id(top_module, mapped_module_io_info.first));
+ VTR_ASSERT(size_t(-1) != mapped_module_io_info.second);
+
+ /* Ensure that IO index is in range */
+ BasicPort module_mapped_io_port = module_manager.module_port(top_module, mapped_module_io_info.first);
+ size_t io_index = mapped_module_io_info.second;
+
+ /* Set the port pin index */
+ VTR_ASSERT(io_index < module_mapped_io_port.get_width());
+ module_mapped_io_port.set_width(io_index, io_index);
+
+ /* The block may be renamed as it contains special characters which violate Verilog syntax */
+ std::string block_name = atom_ctx.nlist.block_name(atom_blk);
+ if (true == netlist_annotation.is_block_renamed(atom_blk)) {
+ block_name = netlist_annotation.block_name(atom_blk);
+ }
+
+ /* Create the port for benchmark I/O, due to BLIF benchmark, each I/O always has a size of 1
+ * In addition, the input and output ports may have different postfix in naming
+ * due to verification context! Here, we give full customization on naming
+ */
+ BasicPort benchmark_io_port;
+ if (AtomBlockType::INPAD == atom_ctx.nlist.block_type(atom_blk)) {
+ benchmark_io_port.set_name(std::string(block_name + io_input_port_name_postfix));
+ benchmark_io_port.set_width(1);
+ } else {
+ VTR_ASSERT(AtomBlockType::OUTPAD == atom_ctx.nlist.block_type(atom_blk));
+ benchmark_io_port.set_name(std::string(block_name + io_output_port_name_postfix));
+ benchmark_io_port.set_width(1);
+ }
+
+ io_map.create_io_mapping(module_mapped_io_port,
+ benchmark_io_port,
+ atom_block_type_to_io_map_direction[atom_ctx.nlist.block_type(atom_blk)]);
+ }
+
+ return io_map;
+}
+
+} /* end namespace openfpga */
diff --git a/openfpga/src/fpga_bitstream/build_io_mapping_info.h b/openfpga/src/fpga_bitstream/build_io_mapping_info.h
new file mode 100644
index 000000000..615fafdd8
--- /dev/null
+++ b/openfpga/src/fpga_bitstream/build_io_mapping_info.h
@@ -0,0 +1,33 @@
+#ifndef BUILD_IO_MAPPING_INFO_H
+#define BUILD_IO_MAPPING_INFO_H
+
+/********************************************************************
+ * Include header files that are required by function declaration
+ *******************************************************************/
+#include
+#include
+#include "module_manager.h"
+#include "vpr_context.h"
+#include "io_location_map.h"
+#include "io_map.h"
+#include "vpr_netlist_annotation.h"
+
+/********************************************************************
+ * Function declaration
+ *******************************************************************/
+
+/* begin namespace openfpga */
+namespace openfpga {
+
+IoMap build_fpga_io_mapping_info(const ModuleManager& module_manager,
+ const ModuleId& top_module,
+ const AtomContext& atom_ctx,
+ const PlacementContext& place_ctx,
+ const IoLocationMap& io_location_map,
+ const VprNetlistAnnotation& netlist_annotation,
+ const std::string& io_input_port_name_postfix,
+ const std::string& io_output_port_name_postfix);
+
+} /* end namespace openfpga */
+
+#endif
diff --git a/openfpga/src/fpga_bitstream/write_xml_io_mapping.cpp b/openfpga/src/fpga_bitstream/write_xml_io_mapping.cpp
new file mode 100644
index 000000000..5b3f383b1
--- /dev/null
+++ b/openfpga/src/fpga_bitstream/write_xml_io_mapping.cpp
@@ -0,0 +1,145 @@
+/********************************************************************
+ * This file includes functions that output io mapping information
+ * to files in XML format
+ *******************************************************************/
+#include
+#include
+#include
+
+/* Headers from vtrutil library */
+#include "vtr_assert.h"
+#include "vtr_log.h"
+#include "vtr_time.h"
+
+/* Headers from openfpgautil library */
+#include "openfpga_digest.h"
+
+/* Headers from archopenfpga library */
+#include "openfpga_naming.h"
+
+#include "openfpga_version.h"
+
+#include "build_io_mapping_info.h"
+#include "write_xml_io_mapping.h"
+
+/* begin namespace openfpga */
+namespace openfpga {
+
+/********************************************************************
+ * This function write header information to an I/O mapping file
+ *******************************************************************/
+static
+void write_io_mapping_xml_file_head(std::fstream& fp) {
+ valid_file_stream(fp);
+
+ auto end = std::chrono::system_clock::now();
+ std::time_t end_time = std::chrono::system_clock::to_time_t(end);
+
+ fp << "" << std::endl;
+ fp << std::endl;
+}
+
+/********************************************************************
+ * Write an io mapping pair to an XML file
+ *
+ * Return:
+ * - 0 if succeed
+ * - 1 if critical errors occured
+ *******************************************************************/
+static
+int write_io_mapping_pair_to_xml_file(std::fstream& fp,
+ const IoMap& io_map,
+ const IoMapId& io_map_id,
+ int xml_hierarchy_depth) {
+ if (false == valid_file_stream(fp)) {
+ return 1;
+ }
+
+ write_tab_to_file(fp, xml_hierarchy_depth);
+
+ BasicPort io_port = io_map.io_port(io_map_id);
+ fp << "\n";
+
+ return 0;
+}
+
+/********************************************************************
+ * Write the io mapping information to an XML file
+ * Notes:
+ * - This file is designed for users to learn
+ * - what nets are mapped to each I/O is mapped, io[0] -> netA
+ * - what directionality is applied to each I/O, io[0] -> input
+ *
+ * Return:
+ * - 0 if succeed
+ * - 1 if critical errors occured
+ *******************************************************************/
+int write_io_mapping_to_xml_file(const IoMap& io_map,
+ const std::string& fname,
+ const bool& verbose) {
+ /* Ensure that we have a valid file name */
+ if (true == fname.empty()) {
+ VTR_LOG_ERROR("Received empty file name to output io_mapping!\n\tPlease specify a valid file name.\n");
+ return 1;
+ }
+
+ std::string timer_message = std::string("Write I/O mapping into xml file '") + fname + std::string("'");
+ vtr::ScopedStartFinishTimer timer(timer_message);
+
+ /* Create the file stream */
+ std::fstream fp;
+ fp.open(fname, std::fstream::out | std::fstream::trunc);
+
+ check_file_stream(fname.c_str(), fp);
+
+ /* Write XML head */
+ write_io_mapping_xml_file_head(fp);
+
+ int xml_hierarchy_depth = 0;
+ fp << "\n";
+
+ /* Output fabric bitstream to the file */
+ int status = 0;
+ int io_map_cnt = 0;
+ for (const auto& io_map_id : io_map.io_map()) {
+ status = write_io_mapping_pair_to_xml_file(fp,
+ io_map, io_map_id,
+ xml_hierarchy_depth + 1);
+ io_map_cnt++;
+ if (1 == status) {
+ break;
+ }
+ }
+
+ /* Print an end to the file here */
+ fp << "\n";
+
+ VTR_LOGV(verbose,
+ "Outputted %d I/O mapping to file '%s'\n",
+ io_map_cnt,
+ fname.c_str());
+
+ /* Close file handler */
+ fp.close();
+
+ return status;
+}
+
+} /* end namespace openfpga */
diff --git a/openfpga/src/fpga_bitstream/write_xml_io_mapping.h b/openfpga/src/fpga_bitstream/write_xml_io_mapping.h
new file mode 100644
index 000000000..330106141
--- /dev/null
+++ b/openfpga/src/fpga_bitstream/write_xml_io_mapping.h
@@ -0,0 +1,25 @@
+#ifndef WRITE_XML_IO_MAPPING_H
+#define WRITE_XML_IO_MAPPING_H
+
+/********************************************************************
+ * Include header files that are required by function declaration
+ *******************************************************************/
+#include
+#include
+#include "vpr_context.h"
+#include "io_map.h"
+
+/********************************************************************
+ * Function declaration
+ *******************************************************************/
+
+/* begin namespace openfpga */
+namespace openfpga {
+
+int write_io_mapping_to_xml_file(const IoMap& io_map,
+ const std::string& fname,
+ const bool& verbose);
+
+} /* end namespace openfpga */
+
+#endif
diff --git a/openfpga_flow/openfpga_shell_scripts/write_io_mapping_example_script.openfpga b/openfpga_flow/openfpga_shell_scripts/write_io_mapping_example_script.openfpga
new file mode 100644
index 000000000..62735f8bd
--- /dev/null
+++ b/openfpga_flow/openfpga_shell_scripts/write_io_mapping_example_script.openfpga
@@ -0,0 +1,38 @@
+# Run VPR for the 'and' design
+#--write_rr_graph example_rr_graph.xml
+vpr ${VPR_ARCH_FILE} ${VPR_TESTBENCH_BLIF} --clock_modeling route --absorb_buffer_luts off
+
+# Read OpenFPGA architecture definition
+read_openfpga_arch -f ${OPENFPGA_ARCH_FILE}
+
+# Read OpenFPGA simulation settings
+read_openfpga_simulation_setting -f ${OPENFPGA_SIM_SETTING_FILE}
+
+# Annotate the OpenFPGA architecture to VPR data base
+# to debug use --verbose options
+link_openfpga_arch --activity_file ${ACTIVITY_FILE} --sort_gsb_chan_node_in_edges
+
+# Check and correct any naming conflicts in the BLIF netlist
+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
+
+# Build the module graph
+# - Enabled compression on routing architecture modules
+# - Enabled frame view creation to save runtime and memory
+# Note that this is turned on when bitstream generation
+# is the ONLY purpose of the flow!!!
+build_fabric --compress_routing --frame_view #--verbose
+
+# Write I/O net mapping information
+write_io_mapping --file io_mapping.xml --verbose
+
+# Finish and exit OpenFPGA
+exit
+
+# Note :
+# To run verification at the end of the flow maintain source in ./SRC directory
diff --git a/openfpga_flow/regression_test_scripts/fpga_bitstream_reg_test.sh b/openfpga_flow/regression_test_scripts/fpga_bitstream_reg_test.sh
index db692c002..1f1eac786 100755
--- a/openfpga_flow/regression_test_scripts/fpga_bitstream_reg_test.sh
+++ b/openfpga_flow/regression_test_scripts/fpga_bitstream_reg_test.sh
@@ -25,3 +25,6 @@ run-task fpga_bitstream/repack_wire_lut --debug --show_thread_logs
echo -e "Testing overloading default paths for programmable interconnect when generating bitstream";
run-task fpga_bitstream/overload_mux_default_path --debug --show_thread_logs
+
+echo -e "Testing outputting I/O mapping result to file";
+run-task fpga_bitstream/write_io_mapping --debug --show_thread_logs
diff --git a/openfpga_flow/tasks/fpga_bitstream/write_io_mapping/config/task.conf b/openfpga_flow/tasks/fpga_bitstream/write_io_mapping/config/task.conf
new file mode 100644
index 000000000..a61ec038b
--- /dev/null
+++ b/openfpga_flow/tasks/fpga_bitstream/write_io_mapping/config/task.conf
@@ -0,0 +1,32 @@
+# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+# Configuration file for running experiments
+# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+# timeout_each_job : FPGA Task script splits fpga flow into multiple jobs
+# Each job execute fpga_flow script on combination of architecture & benchmark
+# timeout_each_job is timeout for each job
+# = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
+
+[GENERAL]
+run_engine=openfpga_shell
+power_tech_file = ${PATH:OPENFPGA_PATH}/openfpga_flow/tech/PTM_45nm/45nm.xml
+power_analysis = true
+spice_output=false
+verilog_output=true
+timeout_each_job = 20*60
+fpga_flow=yosys_vpr
+
+[OpenFPGA_SHELL]
+openfpga_shell_template=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_shell_scripts/write_io_mapping_example_script.openfpga
+openfpga_arch_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_arch/k6_frac_N10_40nm_openfpga.xml
+openfpga_sim_setting_file=${PATH:OPENFPGA_PATH}/openfpga_flow/openfpga_simulation_settings/auto_sim_openfpga.xml
+
+[ARCHITECTURES]
+arch0=${PATH:OPENFPGA_PATH}/openfpga_flow/vpr_arch/k6_frac_N10_tileable_40nm.xml
+
+[BENCHMARKS]
+bench0=${PATH:OPENFPGA_PATH}/openfpga_flow/benchmarks/micro_benchmark/and2/and2.v
+
+[SYNTHESIS_PARAM]
+bench0_top = and2
+
+[SCRIPT_PARAM_MIN_ROUTE_CHAN_WIDTH]