[Tool] Add mappable I/O support and enhance I/O support

This commit is contained in:
tangxifan 2020-11-04 20:21:49 -07:00
parent dd86f7f464
commit 37c10f0cb5
11 changed files with 276 additions and 108 deletions

View File

@ -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<enum e_circuit_model_port_type> 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++;
}

View File

@ -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) {

View File

@ -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<CircuitPortId, std::string> port_inv_prefix_;
vtr::vector<CircuitPortId, size_t> port_default_values_;
vtr::vector<CircuitPortId, bool> port_is_io_;
vtr::vector<CircuitPortId, bool> port_is_data_io_;
vtr::vector<CircuitPortId, bool> port_is_mode_select_;
vtr::vector<CircuitPortId, bool> port_is_global_;
vtr::vector<CircuitPortId, bool> port_is_reset_;

View File

@ -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
*/

View File

@ -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");

View File

@ -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;

View File

@ -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 */

View File

@ -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<ModuleId, vtr::vector<ModulePortId, ModulePortId>> port_ids_; /* List of ports for each Module */
vtr::vector<ModuleId, vtr::vector<ModulePortId, BasicPort>> ports_; /* List of ports for each Module */
vtr::vector<ModuleId, vtr::vector<ModulePortId, enum e_module_port_type>> port_types_; /* Type of ports */
vtr::vector<ModuleId, vtr::vector<ModulePortId, bool>> port_is_mappable_io_; /* If the port is mappable to an I/O for user's implementations */
vtr::vector<ModuleId, vtr::vector<ModulePortId, bool>> port_is_wire_; /* If the port is a wire, use for Verilog port definition. If enabled: <port_type> reg <port_name> */
vtr::vector<ModuleId, vtr::vector<ModulePortId, bool>> port_is_register_; /* If the port is a register, use for Verilog port definition. If enabled: <port_type> reg <port_name> */
vtr::vector<ModuleId, vtr::vector<ModulePortId, std::string>> port_preproc_flags_; /* If a port is available only when a pre-processing flag is enabled. This is to record the pre-processing flags */

View File

@ -7,6 +7,7 @@
*******************************************************************/
#include <algorithm>
#include <iomanip>
#include <map>
/* 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<BasicPort> 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<ModulePortId> 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<bool> io_used(module_io_port.get_width(), false);
std::map<ModulePortId, std::vector<bool>> 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<bool>(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<AtomBlockType, ModuleManager::e_module_port_type> 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<ModulePortId, size_t> 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);

View File

@ -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<BasicPort> gpio_ports_to_add;
std::vector<bool> 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<ModulePortId> 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 */

View File

@ -32,6 +32,8 @@
/* begin namespace openfpga */
namespace openfpga {
constexpr std::array<ModuleManager::e_module_port_type, 3> 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);