From 37c10f0cb5f825ffad29e8073b0a94fef97586ac Mon Sep 17 00:00:00 2001 From: tangxifan Date: Wed, 4 Nov 2020 20:21:49 -0700 Subject: [PATCH] [Tool] Add mappable I/O support and enhance I/O support --- .../src/check_circuit_library.cpp | 49 ++++-- .../libarchopenfpga/src/circuit_library.cpp | 17 ++ .../libarchopenfpga/src/circuit_library.h | 48 +++--- .../src/read_xml_circuit_library.cpp | 7 + .../src/write_xml_circuit_library.cpp | 9 + .../fabric/build_fabric_io_location_map.cpp | 40 +++-- openfpga/src/fabric/module_manager.cpp | 22 +++ openfpga/src/fabric/module_manager.h | 7 + .../fpga_verilog/verilog_testbench_utils.cpp | 158 ++++++++++++------ openfpga/src/utils/module_manager_utils.cpp | 25 ++- openfpga/src/utils/module_manager_utils.h | 2 + 11 files changed, 276 insertions(+), 108 deletions(-) diff --git a/libopenfpga/libarchopenfpga/src/check_circuit_library.cpp b/libopenfpga/libarchopenfpga/src/check_circuit_library.cpp index 7599541fd..b729ef73b 100644 --- a/libopenfpga/libarchopenfpga/src/check_circuit_library.cpp +++ b/libopenfpga/libarchopenfpga/src/check_circuit_library.cpp @@ -571,31 +571,41 @@ static size_t check_io_circuit_model(const CircuitLibrary& circuit_lib) { size_t num_err = 0; - /* Embedded I/O interface may not have inout port - * iopad_port_types_required.push_back(CIRCUIT_MODEL_PORT_INOUT); - * Some I/Os may not have SRAM port, such as AIB interface - * iopad_port_types_required.push_back(CIRCUIT_MODEL_PORT_SRAM); - */ - std::vector iopad_port_types_required; - iopad_port_types_required.push_back(CIRCUIT_MODEL_PORT_INOUT); - num_err += check_circuit_model_port_required(circuit_lib, CIRCUIT_MODEL_IOPAD, iopad_port_types_required); - /* Each I/O cell must have * - One of the following ports - * - At least 1 ASIC-to-FPGA (A2F) port that is defined as global I/O - * - At least 1 FPGA-to-ASIC (F2A) port that is defined as global I/O! + * - At least 1 ASIC-to-FPGA (A2F) port that is defined as global data I/O + * - At least 1 FPGA-to-ASIC (F2A) port that is defined as global data I/O! * - At least 1 regular port that is non-global which is connected to global routing architecture */ for (const auto& io_model : circuit_lib.models_by_type(CIRCUIT_MODEL_IOPAD)) { - bool has_global_io = false; + bool has_data_io = false; + bool has_data_input_only_io = false; + bool has_data_output_only_io = false; bool has_internal_connection = false; for (const auto& port : circuit_lib.model_ports(io_model)) { - if ( (true == circuit_lib.port_is_io(port) - && (true == circuit_lib.port_is_global(port)))) { - has_global_io = true; + if ( (true == circuit_lib.port_is_io(port)) + && (true == circuit_lib.port_is_data_io(port)) + && (CIRCUIT_MODEL_PORT_INOUT == circuit_lib.port_type(port)) + && (true == circuit_lib.port_is_global(port))) { + has_data_io = true; continue; /* Go to next */ } + if ( (true == circuit_lib.port_is_io(port)) + && (true == circuit_lib.port_is_data_io(port)) + && (CIRCUIT_MODEL_PORT_INPUT == circuit_lib.port_type(port)) + && (true == circuit_lib.port_is_global(port))) { + has_data_input_only_io = true; + continue; /* Go to next */ + } + if ( (true == circuit_lib.port_is_io(port)) + && (true == circuit_lib.port_is_data_io(port)) + && (CIRCUIT_MODEL_PORT_OUTPUT == circuit_lib.port_type(port)) + && (true == circuit_lib.port_is_global(port))) { + has_data_output_only_io = true; + continue; /* Go to next */ + } + if ( (false == circuit_lib.port_is_io(port) && (false == circuit_lib.port_is_global(port))) && (CIRCUIT_MODEL_PORT_SRAM != circuit_lib.port_type(port))) { @@ -604,9 +614,14 @@ size_t check_io_circuit_model(const CircuitLibrary& circuit_lib) { } } - if (false == has_global_io) { + /* Error out when + * - there is no data io, data input-only io and data output-only io + */ + if ( (false == has_data_io) + && (false == has_data_input_only_io) + && (false == has_data_output_only_io)) { VTR_LOGF_ERROR(__FILE__, __LINE__, - "I/O circuit model '%s' does not have any I/O port defined!\n", + "I/O circuit model '%s' does not have any data I/O port defined!\n", circuit_lib.model_name(io_model).c_str()); num_err++; } diff --git a/libopenfpga/libarchopenfpga/src/circuit_library.cpp b/libopenfpga/libarchopenfpga/src/circuit_library.cpp index 55dfe8130..f63cbcfd3 100644 --- a/libopenfpga/libarchopenfpga/src/circuit_library.cpp +++ b/libopenfpga/libarchopenfpga/src/circuit_library.cpp @@ -907,6 +907,13 @@ bool CircuitLibrary::port_is_io(const CircuitPortId& circuit_port_id) const { return port_is_io_[circuit_port_id]; } +/* Return a flag if the port is used in mode-selection purpuse of a circuit model */ +bool CircuitLibrary::port_is_data_io(const CircuitPortId& circuit_port_id) const { + /* validate the circuit_port_id */ + VTR_ASSERT(valid_circuit_port_id(circuit_port_id)); + return port_is_data_io_[circuit_port_id]; +} + /* Return a flag if the port is used in mode-selection purpuse of a circuit model */ bool CircuitLibrary::port_is_mode_select(const CircuitPortId& circuit_port_id) const { /* validate the circuit_port_id */ @@ -1370,6 +1377,7 @@ CircuitPortId CircuitLibrary::add_model_port(const CircuitModelId& model_id, port_inv_prefix_.emplace_back(); port_default_values_.push_back(-1); port_is_io_.push_back(false); + port_is_data_io_.push_back(false); port_is_mode_select_.push_back(false); port_is_global_.push_back(false); port_is_reset_.push_back(false); @@ -1449,6 +1457,15 @@ void CircuitLibrary::set_port_is_io(const CircuitPortId& circuit_port_id, return; } +/* Set the is_mode_select for a port of a circuit model */ +void CircuitLibrary::set_port_is_data_io(const CircuitPortId& circuit_port_id, + const bool& is_data_io) { + /* validate the circuit_port_id */ + VTR_ASSERT(valid_circuit_port_id(circuit_port_id)); + port_is_data_io_[circuit_port_id] = is_data_io; + return; +} + /* Set the is_mode_select for a port of a circuit model */ void CircuitLibrary::set_port_is_mode_select(const CircuitPortId& circuit_port_id, const bool& is_mode_select) { diff --git a/libopenfpga/libarchopenfpga/src/circuit_library.h b/libopenfpga/libarchopenfpga/src/circuit_library.h index bd082f738..415d1d4be 100644 --- a/libopenfpga/libarchopenfpga/src/circuit_library.h +++ b/libopenfpga/libarchopenfpga/src/circuit_library.h @@ -79,27 +79,29 @@ * 2. pass_gate_logic_model_id_: specify the id of circuit model for the pass gate logic * * ------ Port information ------ - * 1. port_ids_: unique id of ports belonging to a circuit model - * 1. port_model_ids_: unique id of the parent circuit model for the port - * 2. port_types_: types of ports belonging to a circuit model - * 3. port_sizes_: width of ports belonging to a circuit model - * 4. port_prefix_: prefix of a port when instance of a circuit model - * 5. port_lib_names_: port name in the standard cell library, only used when explicit_port_mapping is enabled - * 6. port_inv_prefix_: the prefix to be added for the inverted port. This is mainly used by SRAM ports, which have an coupled inverterd port - * 7. port_is_mode_select: specify if this port is used to select operating modes of the circuit model - * 8. port_is_global: specify if this port is a global signal shared by other circuit model - * 9. port_is_reset: specify if this port is a reset signal which needs special pulse widths in testbenches - * 10. port_is_set: specify if this port is a set signal which needs special pulse widths in testbenches - * 11. port_is_config_enable: specify if this port is a config_enable signal which needs special pulse widths in testbenches - * 12. port_is_prog: specify if this port is for FPGA programming use which needs special pulse widths in testbenches - * 13. port_tri_state_model_name: the name of circuit model linked to tri-state the port - * 14. port_tri_state_model_ids_: the Id of circuit model linked to tri-state the port - * 15. port_inv_model_names_: the name of inverter circuit model linked to the port - * 16. port_inv_model_ids_: the Id of inverter circuit model linked to the port - * 17. port_tri_state_map_: only applicable to inputs of LUTs, the tri-state map applied to each pin of this port - * 18. port_lut_frac_level_: only applicable to outputs of LUTs, indicate which level of outputs inside LUT multiplexing structure will be used - * 19. port_lut_output_mask_: only applicable to outputs of LUTs, indicate which output at an internal level of LUT multiplexing structure will be used - * 20. port_sram_orgz_: only applicable to SRAM ports, indicate how the SRAMs will be organized, either memory decoders or scan-chains + * - port_ids_: unique id of ports belonging to a circuit model + * - port_model_ids_: unique id of the parent circuit model for the port + * - port_types_: types of ports belonging to a circuit model + * - port_sizes_: width of ports belonging to a circuit model + * - port_prefix_: prefix of a port when instance of a circuit model + * - port_lib_names_: port name in the standard cell library, only used when explicit_port_mapping is enabled + * - port_inv_prefix_: the prefix to be added for the inverted port. This is mainly used by SRAM ports, which have an coupled inverterd port + * - port_is_mode_select: specify if this port is used to select operating modes of the circuit model + * - port_is_io: specify if this port is an io port + * - port_is_data_io: specify if this port is an io port that can be mapped to a signal from netlist + * - port_is_global: specify if this port is a global signal shared by other circuit model + * - port_is_reset: specify if this port is a reset signal which needs special pulse widths in testbenches + * - port_is_set: specify if this port is a set signal which needs special pulse widths in testbenches + * - port_is_config_enable: specify if this port is a config_enable signal which needs special pulse widths in testbenches + * - port_is_prog: specify if this port is for FPGA programming use which needs special pulse widths in testbenches + * - port_tri_state_model_name: the name of circuit model linked to tri-state the port + * - port_tri_state_model_ids_: the Id of circuit model linked to tri-state the port + * - port_inv_model_names_: the name of inverter circuit model linked to the port + * - port_inv_model_ids_: the Id of inverter circuit model linked to the port + * - port_tri_state_map_: only applicable to inputs of LUTs, the tri-state map applied to each pin of this port + * - port_lut_frac_level_: only applicable to outputs of LUTs, indicate which level of outputs inside LUT multiplexing structure will be used + * - port_lut_output_mask_: only applicable to outputs of LUTs, indicate which output at an internal level of LUT multiplexing structure will be used + * - port_sram_orgz_: only applicable to SRAM ports, indicate how the SRAMs will be organized, either memory decoders or scan-chains * * ------ Delay information ------ * 1. delay_types_: type of pin-to-pin delay, either rising_edge of falling_edge @@ -279,6 +281,7 @@ class CircuitLibrary { std::string port_inv_prefix(const CircuitPortId& circuit_port_id) const; size_t port_default_value(const CircuitPortId& circuit_port_id) const; bool port_is_io(const CircuitPortId& circuit_port_id) const; + bool port_is_data_io(const CircuitPortId& circuit_port_id) const; bool port_is_mode_select(const CircuitPortId& circuit_port_id) const; bool port_is_global(const CircuitPortId& circuit_port_id) const; bool port_is_reset(const CircuitPortId& circuit_port_id) const; @@ -354,6 +357,8 @@ class CircuitLibrary { const size_t& default_val); void set_port_is_io(const CircuitPortId& circuit_port_id, const bool& is_io); + void set_port_is_data_io(const CircuitPortId& circuit_port_id, + const bool& is_data_io); void set_port_is_mode_select(const CircuitPortId& circuit_port_id, const bool& is_mode_select); void set_port_is_global(const CircuitPortId& circuit_port_id, @@ -545,6 +550,7 @@ class CircuitLibrary { vtr::vector port_inv_prefix_; vtr::vector port_default_values_; vtr::vector port_is_io_; + vtr::vector port_is_data_io_; vtr::vector port_is_mode_select_; vtr::vector port_is_global_; vtr::vector port_is_reset_; diff --git a/libopenfpga/libarchopenfpga/src/read_xml_circuit_library.cpp b/libopenfpga/libarchopenfpga/src/read_xml_circuit_library.cpp index 86b1de94e..c574e890e 100644 --- a/libopenfpga/libarchopenfpga/src/read_xml_circuit_library.cpp +++ b/libopenfpga/libarchopenfpga/src/read_xml_circuit_library.cpp @@ -418,6 +418,13 @@ void read_xml_circuit_port(pugi::xml_node& xml_port, */ circuit_lib.set_port_is_io(port, get_attribute(xml_port, "is_io", loc_data, pugiutil::ReqOpt::OPTIONAL).as_bool(false)); + /* Identify if the port is a data io, ONLY applicable to I/O port + * By default, it will NOT be a data io port + */ + if (true == circuit_lib.port_is_io(port)) { + circuit_lib.set_port_is_data_io(port, get_attribute(xml_port, "is_data_io", loc_data, pugiutil::ReqOpt::OPTIONAL).as_bool(false)); + } + /* Identify if the port is for mode selection, this is only applicable to SRAM ports. * By default, it will NOT be a mode selection port */ diff --git a/libopenfpga/libarchopenfpga/src/write_xml_circuit_library.cpp b/libopenfpga/libarchopenfpga/src/write_xml_circuit_library.cpp index b141b0fe2..954f9044f 100644 --- a/libopenfpga/libarchopenfpga/src/write_xml_circuit_library.cpp +++ b/libopenfpga/libarchopenfpga/src/write_xml_circuit_library.cpp @@ -186,6 +186,15 @@ void write_xml_circuit_port(std::fstream& fp, } } + /* I/O port attributes */ + if (true == circuit_lib.port_is_io(port)) { + write_xml_attribute(fp, "is_io", "true"); + } + + if (true == circuit_lib.port_is_data_io(port)) { + write_xml_attribute(fp, "is_data_io", "true"); + } + /* Global, reset, set port attributes */ if (true == circuit_lib.port_is_global(port)) { write_xml_attribute(fp, "is_global", "true"); diff --git a/openfpga/src/fabric/build_fabric_io_location_map.cpp b/openfpga/src/fabric/build_fabric_io_location_map.cpp index a99d890e5..ac63e3354 100644 --- a/openfpga/src/fabric/build_fabric_io_location_map.cpp +++ b/openfpga/src/fabric/build_fabric_io_location_map.cpp @@ -18,6 +18,7 @@ #include "openfpga_reserved_words.h" #include "openfpga_naming.h" +#include "module_manager_utils.h" #include "openfpga_device_grid_utils.h" #include "build_fabric_io_location_map.h" @@ -74,16 +75,25 @@ IoLocationMap build_fabric_io_location_map(const ModuleManager& module_manager, * Note: if you change the GPIO function, you should update here as well! */ for (int z = 0; z < grids[io_coordinate.x()][io_coordinate.y()].type->capacity; ++z) { - for (const BasicPort& gpio_port : module_manager.module_ports_by_type(grid_module, ModuleManager::MODULE_GPIO_PORT)) { - auto curr_io_index = io_counter.find(gpio_port.get_name()); - /* Index always start from zero */ - if (curr_io_index == io_counter.end()) { - io_counter[gpio_port.get_name()] = 0; + 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(grid_module, module_io_port_type)) { + /* Only care mappable I/O */ + if (false == module_manager.port_is_mappable_io(grid_module, gpio_port_id)) { + continue; + } + + const BasicPort& gpio_port = module_manager.module_port(grid_module, gpio_port_id); + + auto curr_io_index = io_counter.find(gpio_port.get_name()); + /* Index always start from zero */ + if (curr_io_index == io_counter.end()) { + io_counter[gpio_port.get_name()] = 0; + } + io_location_map.set_io_index(io_coordinate.x(), io_coordinate.y(), z, + gpio_port.get_name(), + io_counter[gpio_port.get_name()]); + io_counter[gpio_port.get_name()]++; } - io_location_map.set_io_index(io_coordinate.x(), io_coordinate.y(), z, - gpio_port.get_name(), - io_counter[gpio_port.get_name()]); - io_counter[gpio_port.get_name()]++; } } } @@ -94,8 +104,16 @@ IoLocationMap build_fabric_io_location_map(const ModuleManager& module_manager, ModuleId top_module = module_manager.find_module(top_module_name); VTR_ASSERT(true == module_manager.valid_module_id(top_module)); - for (const BasicPort& gpio_port : module_manager.module_ports_by_type(top_module, ModuleManager::MODULE_GPIO_PORT)) { - VTR_ASSERT(io_counter[gpio_port.get_name()] == gpio_port.get_width()); + 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; + } + + const BasicPort& gpio_port = module_manager.module_port(top_module, gpio_port_id); + VTR_ASSERT(io_counter[gpio_port.get_name()] == gpio_port.get_width()); + } } return io_location_map; diff --git a/openfpga/src/fabric/module_manager.cpp b/openfpga/src/fabric/module_manager.cpp index d888d1a2f..87fa0ca5c 100644 --- a/openfpga/src/fabric/module_manager.cpp +++ b/openfpga/src/fabric/module_manager.cpp @@ -286,6 +286,12 @@ size_t ModuleManager::instance_id(const ModuleId& parent_module, const ModuleId& return size_t(-1); } +ModuleManager::e_module_port_type ModuleManager::port_type(const ModuleId& module, const ModulePortId& port) const { + /* validate both module id and port id*/ + VTR_ASSERT(valid_module_port_id(module, port)); + return port_types_[module][port]; +} + /* Find if a port is a wire connection */ bool ModuleManager::port_is_wire(const ModuleId& module, const ModulePortId& port) const { /* validate both module id and port id*/ @@ -293,6 +299,13 @@ bool ModuleManager::port_is_wire(const ModuleId& module, const ModulePortId& por return port_is_wire_[module][port]; } +/* Find if a port is a mappable i/o */ +bool ModuleManager::port_is_mappable_io(const ModuleId& module, const ModulePortId& port) const { + /* validate both module id and port id*/ + VTR_ASSERT(valid_module_port_id(module, port)); + return port_is_mappable_io_[module][port]; +} + /* Find if a port is register */ bool ModuleManager::port_is_register(const ModuleId& module, const ModulePortId& port) const { /* validate both module id and port id*/ @@ -529,6 +542,7 @@ ModuleId ModuleManager::add_module(const std::string& name) { ports_.emplace_back(); port_types_.emplace_back(); port_is_wire_.emplace_back(); + port_is_mappable_io_.emplace_back(); port_is_register_.emplace_back(); port_preproc_flags_.emplace_back(); @@ -573,6 +587,7 @@ ModulePortId ModuleManager::add_port(const ModuleId& module, ports_[module].push_back(port_info); port_types_[module].push_back(port_type); port_is_wire_[module].push_back(false); + port_is_mappable_io_[module].push_back(false); port_is_register_[module].push_back(false); port_preproc_flags_[module].emplace_back(); /* Create an empty string for the pre-processing flags */ @@ -617,6 +632,13 @@ void ModuleManager::set_port_is_wire(const ModuleId& module, const std::string& port_is_wire_[module][port] = is_wire; } +/* Set a port to be a mappable I/O */ +void ModuleManager::set_port_is_mappable_io(const ModuleId& module, const ModulePortId& port_id, const bool& is_mappable_io) { + /* Must find something, otherwise drop an error */ + VTR_ASSERT(valid_module_port_id(module, port_id)); + port_is_mappable_io_[module][port_id] = is_mappable_io; +} + /* Set a port to be a register */ void ModuleManager::set_port_is_register(const ModuleId& module, const std::string& port_name, const bool& is_register) { /* Find the port */ diff --git a/openfpga/src/fabric/module_manager.h b/openfpga/src/fabric/module_manager.h index 9f39b23db..d58442c73 100644 --- a/openfpga/src/fabric/module_manager.h +++ b/openfpga/src/fabric/module_manager.h @@ -184,8 +184,12 @@ class ModuleManager { /* Find the instance id of a given instance name */ size_t instance_id(const ModuleId& parent_module, const ModuleId& child_module, const std::string& instance_name) const; + /* Find the type of a port */ + ModuleManager::e_module_port_type port_type(const ModuleId& module, const ModulePortId& port) const; /* Find if a port is a wire connection */ bool port_is_wire(const ModuleId& module, const ModulePortId& port) const; + /* Find if a port is mappable to an I/O from users' implementations */ + bool port_is_mappable_io(const ModuleId& module, const ModulePortId& port) const; /* Find if a port is register */ bool port_is_register(const ModuleId& module, const ModulePortId& port) const; /* Return the pre-processing flag of a port */ @@ -238,6 +242,8 @@ class ModuleManager { void set_module_usage(const ModuleId& module, const e_module_usage_type& usage); /* Set a port to be a wire */ void set_port_is_wire(const ModuleId& module, const std::string& port_name, const bool& is_wire); + /* Set a port to be mappable to an I/O from users' implemenations */ + void set_port_is_mappable_io(const ModuleId& module, const ModulePortId& port_id, const bool& is_mappable_io); /* Set a port to be a register */ void set_port_is_register(const ModuleId& module, const std::string& port_name, const bool& is_register); /* Set the preprocessing flag for a port */ @@ -356,6 +362,7 @@ class ModuleManager { vtr::vector> port_ids_; /* List of ports for each Module */ vtr::vector> ports_; /* List of ports for each Module */ vtr::vector> port_types_; /* Type of ports */ + vtr::vector> port_is_mappable_io_; /* If the port is mappable to an I/O for user's implementations */ vtr::vector> port_is_wire_; /* If the port is a wire, use for Verilog port definition. If enabled: reg */ vtr::vector> port_is_register_; /* If the port is a register, use for Verilog port definition. If enabled: reg */ vtr::vector> port_preproc_flags_; /* If a port is available only when a pre-processing flag is enabled. This is to record the pre-processing flags */ diff --git a/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp b/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp index afd4cff8d..6f08a1c85 100644 --- a/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp +++ b/openfpga/src/fpga_verilog/verilog_testbench_utils.cpp @@ -7,6 +7,7 @@ *******************************************************************/ #include #include +#include /* Headers from vtrutil library */ #include "vtr_assert.h" @@ -17,6 +18,8 @@ #include "verilog_port_types.h" +#include "module_manager_utils.h" + #include "verilog_constants.h" #include "verilog_writer_utils.h" #include "verilog_testbench_utils.h" @@ -147,84 +150,129 @@ void print_verilog_testbench_connect_fpga_ios(std::fstream& fp, /* Validate the file stream */ valid_file_stream(fp); - /* In this function, we support only 1 type of I/Os */ - std::vector module_io_ports = module_manager.module_ports_by_type(top_module, ModuleManager::MODULE_GPIO_PORT); + /* 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); + } + } /* Keep tracking which I/Os have been used */ - for (const BasicPort& module_io_port : module_io_ports) { - std::vector io_used(module_io_port.get_width(), false); + std::map> io_used; + 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); + io_used[module_io_port_id] = std::vector(module_io_port.get_width(), false); + } - /* See if this I/O should be wired to a benchmark input/output */ - /* Add signals from blif benchmark and short-wire them to FPGA I/O PADs - * This brings convenience to checking functionality - */ - print_verilog_comment(fp, std::string("----- Link BLIF Benchmark I/Os to FPGA I/Os -----")); + /* 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; - 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; - } + /* See if this I/O should be wired to a benchmark input/output */ + /* Add signals from blif benchmark and short-wire them to FPGA I/O PADs + * This brings convenience to checking functionality + */ + print_verilog_comment(fp, std::string("----- Link BLIF Benchmark I/Os to FPGA I/Os -----")); + + 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 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()); + 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) == io_index) { + if (size_t(-1) == temp_io_index) { continue; } - /* Ensure that IO index is in range */ - BasicPort module_mapped_io_port = module_io_port; - /* 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); - print_verilog_comment(fp, std::string("----- Blif Benchmark input " + block_name + " is mapped to FPGA IOPAD " + module_mapped_io_port.get_name() + "[" + std::to_string(io_index) + "] -----")); - print_verilog_wire_connection(fp, module_mapped_io_port, benchmark_io_port, false); - } 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); - print_verilog_comment(fp, std::string("----- Blif Benchmark output " + block_name + " is mapped to FPGA IOPAD " + module_mapped_io_port.get_name() + "[" + std::to_string(io_index) + "] -----")); - print_verilog_wire_connection(fp, benchmark_io_port, module_mapped_io_port, false); + /* 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; } - /* Mark this I/O has been used/wired */ - io_used[io_index] = true; + /* 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); + print_verilog_comment(fp, std::string("----- Blif Benchmark input " + block_name + " is mapped to FPGA IOPAD " + module_mapped_io_port.get_name() + "[" + std::to_string(io_index) + "] -----")); + print_verilog_wire_connection(fp, module_mapped_io_port, benchmark_io_port, false); + } 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); + print_verilog_comment(fp, std::string("----- Blif Benchmark output " + block_name + " is mapped to FPGA IOPAD " + module_mapped_io_port.get_name() + "[" + std::to_string(io_index) + "] -----")); + print_verilog_wire_connection(fp, benchmark_io_port, module_mapped_io_port, false); + } + + /* Mark this I/O has been used/wired */ + io_used[mapped_module_io_info.first][io_index] = true; + /* Add an empty line as a splitter */ fp << std::endl; + } - /* Wire the unused iopads to a constant */ - print_verilog_comment(fp, std::string("----- Wire unused FPGA I/Os to constants -----")); - for (size_t io_index = 0; io_index < io_used.size(); ++io_index) { + /* Wire the unused iopads to a constant */ + print_verilog_comment(fp, std::string("----- Wire unused FPGA I/Os to constants -----")); + for (const ModulePortId& module_io_port_id : module_io_ports) { + for (size_t io_index = 0; io_index < io_used[module_io_port_id].size(); ++io_index) { /* Bypass used iopads */ - if (true == io_used[io_index]) { + if (true == io_used[module_io_port_id][io_index]) { continue; } /* Wire to a contant */ - BasicPort module_unused_io_port = module_io_port; + BasicPort module_unused_io_port = module_manager.module_port(top_module, module_io_port_id); /* Set the port pin index */ module_unused_io_port.set_width(io_index, io_index); diff --git a/openfpga/src/utils/module_manager_utils.cpp b/openfpga/src/utils/module_manager_utils.cpp index 7d56430b6..c11a7f92b 100644 --- a/openfpga/src/utils/module_manager_utils.cpp +++ b/openfpga/src/utils/module_manager_utils.cpp @@ -191,7 +191,11 @@ ModuleId add_circuit_model_to_module_manager(ModuleManager& module_manager, for (const auto& kv : port_type2type_map) { for (const auto& port : circuit_lib.model_ports_by_type(circuit_model, kv.first, true)) { BasicPort port_info(circuit_lib.port_prefix(port), circuit_lib.port_size(port)); - module_manager.add_port(module, port_info, kv.second); + ModulePortId module_port = module_manager.add_port(module, port_info, kv.second); + /* Specify if the port can be mapped to an data signal */ + if (true == circuit_lib.port_is_data_io(port)) { + module_manager.set_port_is_mappable_io(module, module_port, true); + } } } @@ -1454,20 +1458,27 @@ void add_module_io_ports_from_child_modules(ModuleManager& module_manager, const ModuleId& module_id, const ModuleManager::e_module_port_type& module_port_type) { std::vector gpio_ports_to_add; + std::vector mappable_gpio_ports; /* Iterate over the child modules */ for (const ModuleId& child : module_manager.child_modules(module_id)) { /* Iterate over the child instances */ for (size_t i = 0; i < module_manager.num_instance(module_id, child); ++i) { /* Find all the global ports, whose port type is special */ - for (BasicPort gpio_port : module_manager.module_ports_by_type(child, module_port_type)) { + for (const ModulePortId& gpio_port_id : module_manager.module_port_ids_by_type(child, module_port_type)) { + const BasicPort& gpio_port = module_manager.module_port(child, gpio_port_id); /* If this port is not mergeable, we update the list */ bool is_mergeable = false; - for (BasicPort& gpio_port_to_add : gpio_ports_to_add) { + for (size_t i_gpio_port_to_add = 0; i_gpio_port_to_add < gpio_ports_to_add.size(); ++i_gpio_port_to_add) { + BasicPort& gpio_port_to_add = gpio_ports_to_add[i_gpio_port_to_add]; if (false == gpio_port_to_add.mergeable(gpio_port)) { continue; } is_mergeable = true; + /* Mappable I/O property must match! Mismatch rarely happened + * but should error out avoid silent bugs! + */ + VTR_ASSERT(module_manager.port_is_mappable_io(child, gpio_port_id) == mappable_gpio_ports[i_gpio_port_to_add]); /* For mergeable ports, we combine the port * Note: do NOT use the merge() method! * the GPIO ports should be accumulated by the sizes of ports @@ -1479,6 +1490,8 @@ void add_module_io_ports_from_child_modules(ModuleManager& module_manager, if (false == is_mergeable) { /* Reach here, this is an unique gpio port, update the list */ gpio_ports_to_add.push_back(gpio_port); + /* If the gpio port is a mappable I/O, we should herit from the child module */ + mappable_gpio_ports.push_back(module_manager.port_is_mappable_io(child, gpio_port_id)); } } } @@ -1487,9 +1500,13 @@ void add_module_io_ports_from_child_modules(ModuleManager& module_manager, /* Record the port id for each type of GPIO port */ std::vector gpio_port_ids; /* Add the gpio ports for the module */ - for (const BasicPort& gpio_port_to_add : gpio_ports_to_add) { + for (size_t iport = 0; iport < gpio_ports_to_add.size(); ++iport) { + const BasicPort& gpio_port_to_add = gpio_ports_to_add[iport]; ModulePortId port_id = module_manager.add_port(module_id, gpio_port_to_add, module_port_type); gpio_port_ids.push_back(port_id); + if (true == mappable_gpio_ports[iport]) { + module_manager.set_port_is_mappable_io(module_id, port_id, true); + } } /* Set up a counter for each type of GPIO port */ diff --git a/openfpga/src/utils/module_manager_utils.h b/openfpga/src/utils/module_manager_utils.h index 28913e673..63787bd34 100644 --- a/openfpga/src/utils/module_manager_utils.h +++ b/openfpga/src/utils/module_manager_utils.h @@ -32,6 +32,8 @@ /* begin namespace openfpga */ namespace openfpga { +constexpr std::array MODULE_IO_PORT_TYPES = {ModuleManager::MODULE_GPIN_PORT, ModuleManager::MODULE_GPOUT_PORT, ModuleManager::MODULE_GPIO_PORT}; + void reserve_module_manager_module_nets(ModuleManager& module_manager, const ModuleId& module);