diff --git a/openfpga/src/base/openfpga_build_fabric.cpp b/openfpga/src/base/openfpga_build_fabric.cpp index 879c127d6..5c3d8a69d 100644 --- a/openfpga/src/base/openfpga_build_fabric.cpp +++ b/openfpga/src/base/openfpga_build_fabric.cpp @@ -72,6 +72,7 @@ void build_fabric(OpenfpgaContext& openfpga_context, openfpga_context.mutable_module_graph() = build_device_module_graph(g_vpr_ctx.device(), const_cast(openfpga_context), + cmd_context.option_enable(cmd, opt_compress_routing), cmd_context.option_enable(cmd, opt_duplicate_grid_pin), cmd_context.option_enable(cmd, opt_verbose)); } diff --git a/openfpga/src/fabric/build_device_module.cpp b/openfpga/src/fabric/build_device_module.cpp index fd7f1f31e..db3d3dfda 100644 --- a/openfpga/src/fabric/build_device_module.cpp +++ b/openfpga/src/fabric/build_device_module.cpp @@ -15,7 +15,7 @@ #include "build_wire_modules.h" #include "build_memory_modules.h" #include "build_grid_modules.h" -//#include "build_routing_modules.h" +#include "build_routing_modules.h" //#include "build_top_module.h" #include "build_device_module.h" @@ -28,6 +28,7 @@ namespace openfpga { *******************************************************************/ ModuleManager build_device_module_graph(const DeviceContext& vpr_device_ctx, const OpenfpgaContext& openfpga_ctx, + const bool& compress_routing, const bool& duplicate_grid_pin, const bool& verbose) { vtr::ScopedStartFinishTimer timer("Build fabric module graph"); @@ -76,17 +77,24 @@ ModuleManager build_device_module_graph(const DeviceContext& vpr_device_ctx, openfpga_ctx.arch().config_protocol.type(), sram_model, duplicate_grid_pin, verbose); - //if (TRUE == vpr_setup.FPGA_SPICE_Opts.compact_routing_hierarchy) { - // build_unique_routing_modules(module_manager, L_device_rr_gsb, arch.spice->circuit_lib, - // arch.sram_inf.verilog_sram_inf_orgz->type, sram_model, - // vpr_setup.RoutingArch, rr_switches); - //} else { - // VTR_ASSERT(FALSE == vpr_setup.FPGA_SPICE_Opts.compact_routing_hierarchy); - // build_flatten_routing_modules(module_manager, L_device_rr_gsb, arch.spice->circuit_lib, - // arch.sram_inf.verilog_sram_inf_orgz->type, sram_model, - // vpr_setup.RoutingArch, rr_switches); - //} - + if (true == compress_routing) { + build_unique_routing_modules(module_manager, + vpr_device_ctx, + openfpga_ctx.vpr_device_annotation(), + openfpga_ctx.device_rr_gsb(), + openfpga_ctx.arch().circuit_lib, + openfpga_ctx.arch().config_protocol.type(), + sram_model, verbose); + } else { + VTR_ASSERT_SAFE(false == compress_routing); + build_flatten_routing_modules(module_manager, + vpr_device_ctx, + openfpga_ctx.vpr_device_annotation(), + openfpga_ctx.device_rr_gsb(), + openfpga_ctx.arch().circuit_lib, + openfpga_ctx.arch().config_protocol.type(), + sram_model, verbose); + } /* Build FPGA fabric top-level module */ //build_top_module(module_manager, arch.spice->circuit_lib, diff --git a/openfpga/src/fabric/build_device_module.h b/openfpga/src/fabric/build_device_module.h index 6830ca82b..fd585d986 100644 --- a/openfpga/src/fabric/build_device_module.h +++ b/openfpga/src/fabric/build_device_module.h @@ -16,6 +16,7 @@ namespace openfpga { ModuleManager build_device_module_graph(const DeviceContext& vpr_device_ctx, const OpenfpgaContext& openfpga_ctx, + const bool& compress_routing, const bool& duplicate_grid_pin, const bool& verbose); diff --git a/openfpga/src/fabric/build_routing_modules.cpp b/openfpga/src/fabric/build_routing_modules.cpp new file mode 100644 index 000000000..a08a0680f --- /dev/null +++ b/openfpga/src/fabric/build_routing_modules.cpp @@ -0,0 +1,1001 @@ +/******************************************************************** + * This file includes functions that are used to build modules + * for global routing architecture of a FPGA fabric + * Covering: + * 1. Connection blocks + * 2. Switch blocks + *******************************************************************/ +#include + +/* Headers from vtrutil library */ +#include "vtr_assert.h" +#include "vtr_geometry.h" +#include "vtr_log.h" +#include "vtr_time.h" + +/* Headers from openfpgautil library */ +#include "openfpga_side_manager.h" + +#include "openfpga_reserved_words.h" +#include "openfpga_naming.h" + +#include "openfpga_rr_graph_utils.h" +#include "module_manager_utils.h" +#include "build_module_graph_utils.h" +#include "build_routing_module_utils.h" + +#include "build_routing_modules.h" + +/* begin namespace openfpga */ +namespace openfpga { + +/********************************************************************* + * Generate a short interconneciton in switch box + * There are two cases should be noticed. + * 1. The actual fan-in of cur_rr_node is 0. In this case, + the cur_rr_node need to be short connected to itself + which is on the opposite side of this switch block + * 2. The actual fan-in of cur_rr_node is 0. In this case, + * The cur_rr_node need to connected to the drive_rr_node + ********************************************************************/ +static +void build_switch_block_module_short_interc(ModuleManager& module_manager, + const ModuleId& sb_module, + const RRGraph& rr_graph, + const RRGSB& rr_gsb, + const e_side& chan_side, + const RRNodeId& cur_rr_node, + const RRNodeId& drive_rr_node, + const std::map& input_port_to_module_nets) { + /* Find the name of output port */ + ModulePortId output_port_id = find_switch_block_module_chan_port(module_manager, sb_module, + rr_graph, rr_gsb, + chan_side, cur_rr_node, OUT_PORT); + enum e_side input_pin_side = chan_side; + int index = -1; + + /* Generate the input port object */ + switch (rr_graph.node_type(drive_rr_node)) { + case OPIN: { + rr_gsb.get_node_side_and_index(rr_graph, drive_rr_node, IN_PORT, input_pin_side, index); + break; + } + case CHANX: + case CHANY: { + /* This should be an input in the data structure of RRGSB */ + if (cur_rr_node == drive_rr_node) { + /* To be strict, the input should locate on the opposite side. + * Use the else part if this may change in some architecture. + */ + SideManager side_manager(chan_side); + input_pin_side = side_manager.get_opposite(); + } else { + /* The input could be at any side of the switch block, find it */ + rr_gsb.get_node_side_and_index(rr_graph, drive_rr_node, IN_PORT, input_pin_side, index); + } + break; + } + default: /* SOURCE, IPIN, SINK are invalid*/ + VTR_LOGF_ERROR(__FILE__, __LINE__, "Invalid rr_node type! Should be [OPIN|CHANX|CHANY].\n"); + exit(1); + } + /* Find the name of input port */ + ModulePortId input_port_id = find_switch_block_module_input_port(module_manager, sb_module, rr_graph, rr_gsb, input_pin_side, drive_rr_node); + + /* The input port and output port must match in size */ + BasicPort input_port = module_manager.module_port(sb_module, input_port_id); + BasicPort output_port = module_manager.module_port(sb_module, output_port_id); + VTR_ASSERT(input_port.get_width() == output_port.get_width()); + + /* Create a module net for this short-wire connection */ + for (size_t pin_id = 0; pin_id < input_port.pins().size(); ++pin_id) { + ModuleNetId net = input_port_to_module_nets.at(input_port_id); + /* Skip Configuring the net source, it is done before */ + /* Configure the net sink */ + module_manager.add_module_net_sink(sb_module, net, sb_module, 0, output_port_id, output_port.pins()[pin_id]); + } +} + +/********************************************************************* + * Build a instance of a routing multiplexer as well as + * associated memory modules for a connection inside a switch block + ********************************************************************/ +static +void build_switch_block_mux_module(ModuleManager& module_manager, + const ModuleId& sb_module, + const VprDeviceAnnotation& device_annotation, + const RRGraph& rr_graph, + const RRGSB& rr_gsb, + const CircuitLibrary& circuit_lib, + const e_side& chan_side, + const size_t& chan_node_id, + const RRNodeId& cur_rr_node, + const std::vector& driver_rr_nodes, + const RRSwitchId& switch_index, + const std::map& input_port_to_module_nets) { + /* Check current rr_node is CHANX or CHANY*/ + VTR_ASSERT((CHANX == rr_graph.node_type(cur_rr_node)) || (CHANY == rr_graph.node_type(cur_rr_node))); + + /* Get the circuit model id of the routing multiplexer */ + CircuitModelId mux_model = device_annotation.rr_switch_circuit_model(switch_index); + + /* Find the input size of the implementation of a routing multiplexer */ + size_t datapath_mux_size = driver_rr_nodes.size(); + + /* Find the module name of the multiplexer and try to find it in the module manager */ + std::string mux_module_name = generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, std::string("")); + ModuleId mux_module = module_manager.find_module(mux_module_name); + VTR_ASSERT (true == module_manager.valid_module_id(mux_module)); + + /* Get the MUX instance id from the module manager */ + size_t mux_instance_id = module_manager.num_instance(sb_module, mux_module); + /* Instanciate the MUX Module */ + module_manager.add_child_module(sb_module, mux_module); + + /* Give an instance name: this name should be consistent with the block name given in SDC manager, + * If you want to bind the SDC generation to modules + */ + std::string mux_instance_name = generate_sb_memory_instance_name(SWITCH_BLOCK_MUX_INSTANCE_PREFIX, chan_side, chan_node_id, std::string("")); + module_manager.set_child_instance_name(sb_module, mux_module, mux_instance_id, mux_instance_name); + + /* Generate input ports that are wired to the input bus of the routing multiplexer */ + std::vector sb_input_port_ids = find_switch_block_module_input_ports(module_manager, sb_module, rr_graph, rr_gsb, driver_rr_nodes); + + /* Link input bus port to Switch Block inputs */ + std::vector mux_model_input_ports = circuit_lib.model_ports_by_type(mux_model, CIRCUIT_MODEL_PORT_INPUT, true); + VTR_ASSERT(1 == mux_model_input_ports.size()); + /* Find the module port id of the input port */ + ModulePortId mux_input_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_prefix(mux_model_input_ports[0])); + VTR_ASSERT(true == module_manager.valid_module_port_id(mux_module, mux_input_port_id)); + BasicPort mux_input_port = module_manager.module_port(mux_module, mux_input_port_id); + + /* Check port size should match */ + VTR_ASSERT(mux_input_port.get_width() == sb_input_port_ids.size()); + for (size_t pin_id = 0; pin_id < sb_input_port_ids.size(); ++pin_id) { + /* Use the exising net */ + ModuleNetId net = input_port_to_module_nets.at(sb_input_port_ids[pin_id]); + /* Configure the net source only if it is not yet in the source list */ + if (false == module_manager.net_source_exist(sb_module, net, sb_module, 0, sb_input_port_ids[pin_id], 0)) { + module_manager.add_module_net_source(sb_module, net, sb_module, 0, sb_input_port_ids[pin_id], 0); + } + /* Configure the net sink */ + module_manager.add_module_net_sink(sb_module, net, mux_module, mux_instance_id, mux_input_port_id, mux_input_port.pins()[pin_id]); + } + + /* Link output port to Switch Block outputs */ + std::vector mux_model_output_ports = circuit_lib.model_ports_by_type(mux_model, CIRCUIT_MODEL_PORT_OUTPUT, true); + VTR_ASSERT(1 == mux_model_output_ports.size()); + /* Use the port name convention in the circuit library */ + ModulePortId mux_output_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_prefix(mux_model_output_ports[0])); + VTR_ASSERT(true == module_manager.valid_module_port_id(mux_module, mux_output_port_id)); + BasicPort mux_output_port = module_manager.module_port(mux_module, mux_output_port_id); + ModulePortId sb_output_port_id = find_switch_block_module_chan_port(module_manager, sb_module, rr_graph, rr_gsb, chan_side, cur_rr_node, OUT_PORT); + BasicPort sb_output_port = module_manager.module_port(sb_module, sb_output_port_id); + + /* Check port size should match */ + VTR_ASSERT(sb_output_port.get_width() == mux_output_port.get_width()); + for (size_t pin_id = 0; pin_id < mux_output_port.pins().size(); ++pin_id) { + ModuleNetId net = module_manager.create_module_net(sb_module); + /* Configuring the net source */ + module_manager.add_module_net_source(sb_module, net, mux_module, mux_instance_id, mux_output_port_id, mux_output_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink(sb_module, net, sb_module, 0, sb_output_port_id, sb_output_port.pins()[pin_id]); + } + + /* Instanciate memory modules */ + /* Find the name and module id of the memory module */ + std::string mem_module_name = generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, std::string(MEMORY_MODULE_POSTFIX)); + ModuleId mem_module = module_manager.find_module(mem_module_name); + VTR_ASSERT (true == module_manager.valid_module_id(mem_module)); + + size_t mem_instance_id = module_manager.num_instance(sb_module, mem_module); + module_manager.add_child_module(sb_module, mem_module); + /* Give an instance name: this name should be consistent with the block name given in bitstream manager, + * If you want to bind the bitstream generation to modules + */ + std::string mem_instance_name = generate_sb_memory_instance_name(SWITCH_BLOCK_MEM_INSTANCE_PREFIX, chan_side, chan_node_id, std::string("")); + module_manager.set_child_instance_name(sb_module, mem_module, mem_instance_id, mem_instance_name); + + /* Add nets to connect regular and mode-select SRAM ports to the SRAM port of memory module */ + add_module_nets_between_logic_and_memory_sram_bus(module_manager, sb_module, + mux_module, mux_instance_id, + mem_module, mem_instance_id, + circuit_lib, mux_model); + /* Update memory and instance list */ + module_manager.add_configurable_child(sb_module, mem_module, mem_instance_id); +} + +/********************************************************************* + * Generate child modules for a interconnection inside switch block + * The interconnection could be either a wire or a routing multiplexer, + * which depends on the fan-in of the rr_nodes in the switch block + ********************************************************************/ +static +void build_switch_block_interc_modules(ModuleManager& module_manager, + const ModuleId& sb_module, + const VprDeviceAnnotation& device_annotation, + const RRGraph& rr_graph, + const RRGSB& rr_gsb, + const CircuitLibrary& circuit_lib, + const e_side& chan_side, + const size_t& chan_node_id, + const std::map& input_port_to_module_nets) { + std::vector driver_rr_nodes; + + /* Get the node */ + const RRNodeId& cur_rr_node = rr_gsb.get_chan_node(chan_side, chan_node_id); + + /* Determine if the interc lies inside a channel wire, that is interc between segments */ + if (false == rr_gsb.is_sb_node_passing_wire(rr_graph, chan_side, chan_node_id)) { + driver_rr_nodes = get_rr_graph_configurable_driver_nodes(rr_graph, cur_rr_node); + /* Special: if there are zero-driver nodes. We skip here */ + if (0 == driver_rr_nodes.size()) { + return; + } + } + + if (0 == driver_rr_nodes.size()) { + /* Print a special direct connection*/ + build_switch_block_module_short_interc(module_manager, sb_module, + rr_graph, rr_gsb, + chan_side, cur_rr_node, + cur_rr_node, + input_port_to_module_nets); + } else if (1 == driver_rr_nodes.size()) { + /* Print a direct connection*/ + build_switch_block_module_short_interc(module_manager, sb_module, + rr_graph, rr_gsb, chan_side, cur_rr_node, + driver_rr_nodes[0], + input_port_to_module_nets); + } else if (1 < driver_rr_nodes.size()) { + /* Print the multiplexer, fan_in >= 2 */ + std::vector driver_switches = get_rr_graph_driver_switches(rr_graph, cur_rr_node); + VTR_ASSERT(1 == driver_switches.size()); + build_switch_block_mux_module(module_manager, + sb_module, device_annotation, rr_graph, rr_gsb, + circuit_lib, + chan_side, chan_node_id, cur_rr_node, + driver_rr_nodes, + driver_switches[0], + input_port_to_module_nets); + } /*Nothing should be done else*/ +} + + +/******************************************************************** + * Build a module for a switch block whose detailed description is + * available in a RRGSB object + * A Switch Box module consists of following ports: + * 1. Channel Y [x][y] inputs + * 2. Channel X [x+1][y] inputs + * 3. Channel Y [x][y-1] outputs + * 4. Channel X [x][y] outputs + * 5. Grid[x][y+1] Right side outputs pins + * 6. Grid[x+1][y+1] Left side output pins + * 7. Grid[x+1][y+1] Bottom side output pins + * 8. Grid[x+1][y] Top side output pins + * 9. Grid[x+1][y] Left side output pins + * 10. Grid[x][y] Right side output pins + * 11. Grid[x][y] Top side output pins + * 12. Grid[x][y+1] Bottom side output pins + * + * Location of a Switch Box in FPGA fabric: + * + * -------------- -------------- + * | | | | + * | Grid | ChanY | Grid | + * | [x][y+1] | [x][y+1] | [x+1][y+1] | + * | | | | + * -------------- -------------- + * ---------- + * ChanX | Switch | ChanX + * [x][y] | Box | [x+1][y] + * | [x][y] | + * ---------- + * -------------- -------------- + * | | | | + * | Grid | ChanY | Grid | + * | [x][y] | [x][y] | [x+1][y] | + * | | | | + * -------------- -------------- + * + * Switch Block pin location map + * + * Grid[x][y+1] ChanY[x][y+1] Grid[x+1][y+1] + * right_pins inputs/outputs left_pins + * | ^ | + * | | | + * v v v + * +-----------------------------------------------+ + * | | + * Grid[x][y+1] | | Grid[x+1][y+1] + * bottom_pins---->| |<---- bottom_pins + * | | + * ChanX[x][y] | Switch Box [x][y] | ChanX[x+1][y] + * inputs/outputs<--->| |<---> inputs/outputs + * | | + * Grid[x][y+1] | | Grid[x+1][y+1] + * top_pins---->| |<---- top_pins + * | | + * +-----------------------------------------------+ + * ^ ^ ^ + * | | | + * | v | + * Grid[x][y] ChanY[x][y] Grid[x+1][y] + * right_pins inputs/outputs left_pins + * + * + ********************************************************************/ +static +void build_switch_block_module(ModuleManager& module_manager, + const VprDeviceAnnotation& device_annotation, + const RRGraph& rr_graph, + const CircuitLibrary& circuit_lib, + const e_config_protocol_type& sram_orgz_type, + const CircuitModelId& sram_model, + const RRGSB& rr_gsb, + const bool& verbose) { + /* Create a Module of Switch Block and add to module manager */ + vtr::Point gsb_coordinate(rr_gsb.get_sb_x(), rr_gsb.get_sb_y()); + ModuleId sb_module = module_manager.add_module(generate_switch_block_module_name(gsb_coordinate)); + + VTR_LOGV(verbose, + "Building module '%s'...", + generate_switch_block_module_name(gsb_coordinate).c_str()); + + /* Create a cache (fast look up) for module nets whose source are input ports */ + std::map input_port_to_module_nets; + + /* Add routing channel ports at each side of the GSB */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + + for (size_t itrack = 0; itrack < rr_gsb.get_chan_width(side_manager.get_side()); ++itrack) { + std::string port_name = generate_sb_module_track_port_name(rr_graph.node_type(rr_gsb.get_chan_node(side_manager.get_side(), itrack)), + side_manager.get_side(), itrack, + rr_gsb.get_chan_node_direction(side_manager.get_side(), itrack)); + BasicPort module_port(port_name, 1); /* Every track has a port size of 1 */ + + switch (rr_gsb.get_chan_node_direction(side_manager.get_side(), itrack)) { + case OUT_PORT: + module_manager.add_port(sb_module, module_port, ModuleManager::MODULE_OUTPUT_PORT); + break; + case IN_PORT: { + ModulePortId input_port_id = module_manager.add_port(sb_module, module_port, ModuleManager::MODULE_INPUT_PORT); + /* Cache the input net */ + ModuleNetId net = module_manager.create_module_net(sb_module); + module_manager.add_module_net_source(sb_module, net, sb_module, 0, input_port_id, 0); + input_port_to_module_nets[input_port_id] = net; + break; + } + default: + VTR_LOGF_ERROR(__FILE__, __LINE__, + "Invalid direction of chan[%d][%d]_track[%d]!\n", + rr_gsb.get_sb_x(), rr_gsb.get_sb_y(), itrack); + exit(1); + } + } + /* Dump OPINs of adjacent CLBs */ + for (size_t inode = 0; inode < rr_gsb.get_num_opin_nodes(side_manager.get_side()); ++inode) { + vtr::Point port_coord(rr_graph.node_xlow(rr_gsb.get_opin_node(side_manager.get_side(), inode)), + rr_graph.node_ylow(rr_gsb.get_opin_node(side_manager.get_side(), inode))); + std::string port_name = generate_sb_module_grid_port_name(side_manager.get_side(), + rr_graph.node_side(rr_gsb.get_opin_node(side_manager.get_side(), inode)), + rr_graph.node_pin_num(rr_gsb.get_opin_node(side_manager.get_side(), inode))); + BasicPort module_port(port_name, 1); /* Every grid output has a port size of 1 */ + /* Grid outputs are inputs of switch blocks */ + ModulePortId input_port_id = module_manager.add_port(sb_module, module_port, ModuleManager::MODULE_INPUT_PORT); + + /* Cache the input net */ + ModuleNetId net = module_manager.create_module_net(sb_module); + module_manager.add_module_net_source(sb_module, net, sb_module, 0, input_port_id, 0); + input_port_to_module_nets[input_port_id] = net; + } + } + + /* Add routing multiplexers as child modules */ + for (size_t side = 0; side < rr_gsb.get_num_sides(); ++side) { + SideManager side_manager(side); + for (size_t itrack = 0; itrack < rr_gsb.get_chan_width(side_manager.get_side()); ++itrack) { + /* We care INC_DIRECTION tracks at this side*/ + if (OUT_PORT == rr_gsb.get_chan_node_direction(side_manager.get_side(), itrack)) { + build_switch_block_interc_modules(module_manager, + sb_module, device_annotation, rr_graph, rr_gsb, + circuit_lib, + side_manager.get_side(), + itrack, + input_port_to_module_nets); + } + } + } + + /* Add global ports to the pb_module: + * This is a much easier job after adding sub modules (instances), + * we just need to find all the global ports from the child modules and build a list of it + */ + add_module_global_ports_from_child_modules(module_manager, sb_module); + + /* Count shared SRAM ports from the sub-modules under this Verilog module + * This is a much easier job after adding sub modules (instances), + * we just need to find all the I/O ports from the child modules and build a list of it + */ + size_t module_num_shared_config_bits = find_module_num_shared_config_bits_from_child_modules(module_manager, sb_module); + if (0 < module_num_shared_config_bits) { + add_reserved_sram_ports_to_module_manager(module_manager, sb_module, module_num_shared_config_bits); + } + + /* Count SRAM ports from the sub-modules under this Verilog module + * This is a much easier job after adding sub modules (instances), + * we just need to find all the I/O ports from the child modules and build a list of it + */ + size_t module_num_config_bits = find_module_num_config_bits_from_child_modules(module_manager, sb_module, circuit_lib, sram_model, sram_orgz_type); + if (0 < module_num_config_bits) { + add_sram_ports_to_module_manager(module_manager, sb_module, circuit_lib, sram_model, sram_orgz_type, module_num_config_bits); + } + + /* Add all the nets to connect configuration ports from memory module to primitive modules + * This is a one-shot addition that covers all the memory modules in this primitive module! + */ + if (0 < module_manager.configurable_children(sb_module).size()) { + add_module_nets_memory_config_bus(module_manager, sb_module, + sram_orgz_type, circuit_lib.design_tech_type(sram_model)); + } + + VTR_LOGV(verbose, "Done\n"); +} + +/********************************************************************* + * Print a short interconneciton in connection + ********************************************************************/ +static +void build_connection_block_module_short_interc(ModuleManager& module_manager, + const ModuleId& cb_module, + const RRGraph& rr_graph, + const RRGSB& rr_gsb, + const t_rr_type& cb_type, + const RRNodeId& src_rr_node, + const std::map& input_port_to_module_nets) { + /* Ensure we have only one 1 driver node */ + std::vector driver_rr_nodes = get_rr_graph_configurable_driver_nodes(rr_graph, src_rr_node); + + /* We have OPINs since we may have direct connections: + * These connections should be handled by other functions in the compact_netlist.c + * So we just return here for OPINs + */ + if (0 == driver_rr_nodes.size()) { + return; + } + + /* Find the driver node */ + VTR_ASSERT_SAFE(1 == driver_rr_nodes.size()); + const RRNodeId& driver_rr_node = driver_rr_nodes[0]; + + VTR_ASSERT((CHANX == rr_graph.node_type(driver_rr_node)) || (CHANY == rr_graph.node_type(driver_rr_node))); + + /* Create port description for the routing track middle output */ + ModulePortId input_port_id = find_connection_block_module_chan_port(module_manager, cb_module, rr_graph, rr_gsb, cb_type, driver_rr_node); + + /* Create port description for input pin of a CLB */ + ModulePortId ipin_port_id = find_connection_block_module_ipin_port(module_manager, cb_module, rr_graph, rr_gsb, src_rr_node); + + /* The input port and output port must match in size */ + BasicPort input_port = module_manager.module_port(cb_module, input_port_id); + BasicPort ipin_port = module_manager.module_port(cb_module, ipin_port_id); + VTR_ASSERT(input_port.get_width() == ipin_port.get_width()); + + /* Create a module net for this short-wire connection */ + for (size_t pin_id = 0; pin_id < input_port.pins().size(); ++pin_id) { + ModuleNetId net = input_port_to_module_nets.at(input_port_id); + /* Skip Configuring the net source, it is done before */ + /* Configure the net sink */ + module_manager.add_module_net_sink(cb_module, net, cb_module, 0, ipin_port_id, ipin_port.pins()[pin_id]); + } +} + +/********************************************************************* + * Build a instance of a routing multiplexer as well as + * associated memory modules for a connection inside a connection block + ********************************************************************/ +static +void build_connection_block_mux_module(ModuleManager& module_manager, + const ModuleId& cb_module, + const VprDeviceAnnotation& device_annotation, + const RRGraph& rr_graph, + const RRGSB& rr_gsb, + const t_rr_type& cb_type, + const CircuitLibrary& circuit_lib, + const e_side& cb_ipin_side, + const size_t& ipin_index, + const std::map& input_port_to_module_nets) { + const RRNodeId& cur_rr_node = rr_gsb.get_ipin_node(cb_ipin_side, ipin_index); + /* Check current rr_node is an input pin of a CLB */ + VTR_ASSERT(IPIN == rr_graph.node_type(cur_rr_node)); + + /* Build a vector of driver rr_nodes */ + std::vector driver_rr_nodes = get_rr_graph_configurable_driver_nodes(rr_graph, cur_rr_node); + + std::vector driver_switches = get_rr_graph_driver_switches(rr_graph, cur_rr_node); + VTR_ASSERT(1 == driver_switches.size()); + + /* Get the circuit model id of the routing multiplexer */ + CircuitModelId mux_model = device_annotation.rr_switch_circuit_model(driver_switches[0]); + + /* Find the input size of the implementation of a routing multiplexer */ + size_t datapath_mux_size = driver_rr_nodes.size(); + + /* Find the module name of the multiplexer and try to find it in the module manager */ + std::string mux_module_name = generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, std::string("")); + ModuleId mux_module = module_manager.find_module(mux_module_name); + VTR_ASSERT (true == module_manager.valid_module_id(mux_module)); + + /* Get the MUX instance id from the module manager */ + size_t mux_instance_id = module_manager.num_instance(cb_module, mux_module); + module_manager.add_child_module(cb_module, mux_module); + + /* Give an instance name: this name should be consistent with the block name given in SDC manager, + * If you want to bind the SDC generation to modules + */ + std::string mux_instance_name = generate_cb_mux_instance_name(CONNECTION_BLOCK_MUX_INSTANCE_PREFIX, rr_graph.node_side(rr_gsb.get_ipin_node(cb_ipin_side, ipin_index)), ipin_index, std::string("")); + module_manager.set_child_instance_name(cb_module, mux_module, mux_instance_id, mux_instance_name); + + /* TODO: Generate input ports that are wired to the input bus of the routing multiplexer */ + std::vector cb_input_port_ids = find_connection_block_module_input_ports(module_manager, cb_module, rr_graph, rr_gsb, cb_type, driver_rr_nodes); + + /* Link input bus port to Switch Block inputs */ + std::vector mux_model_input_ports = circuit_lib.model_ports_by_type(mux_model, CIRCUIT_MODEL_PORT_INPUT, true); + VTR_ASSERT(1 == mux_model_input_ports.size()); + /* Find the module port id of the input port */ + ModulePortId mux_input_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_prefix(mux_model_input_ports[0])); + VTR_ASSERT(true == module_manager.valid_module_port_id(mux_module, mux_input_port_id)); + BasicPort mux_input_port = module_manager.module_port(mux_module, mux_input_port_id); + + /* Check port size should match */ + VTR_ASSERT(mux_input_port.get_width() == cb_input_port_ids.size()); + for (size_t pin_id = 0; pin_id < cb_input_port_ids.size(); ++pin_id) { + /* Use the exising net */ + ModuleNetId net = input_port_to_module_nets.at(cb_input_port_ids[pin_id]); + /* No need to configure the net source since it is already done before */ + /* Configure the net sink */ + module_manager.add_module_net_sink(cb_module, net, mux_module, mux_instance_id, mux_input_port_id, mux_input_port.pins()[pin_id]); + } + + /* Link output port to Switch Block outputs */ + std::vector mux_model_output_ports = circuit_lib.model_ports_by_type(mux_model, CIRCUIT_MODEL_PORT_OUTPUT, true); + VTR_ASSERT(1 == mux_model_output_ports.size()); + /* Use the port name convention in the circuit library */ + ModulePortId mux_output_port_id = module_manager.find_module_port(mux_module, circuit_lib.port_prefix(mux_model_output_ports[0])); + VTR_ASSERT(true == module_manager.valid_module_port_id(mux_module, mux_output_port_id)); + BasicPort mux_output_port = module_manager.module_port(mux_module, mux_output_port_id); + ModulePortId cb_output_port_id = find_connection_block_module_ipin_port(module_manager, cb_module, rr_graph, rr_gsb, cur_rr_node); + BasicPort cb_output_port = module_manager.module_port(cb_module, cb_output_port_id); + + /* Check port size should match */ + VTR_ASSERT(cb_output_port.get_width() == mux_output_port.get_width()); + for (size_t pin_id = 0; pin_id < mux_output_port.pins().size(); ++pin_id) { + ModuleNetId net = module_manager.create_module_net(cb_module); + /* Configuring the net source */ + module_manager.add_module_net_source(cb_module, net, mux_module, mux_instance_id, mux_output_port_id, mux_output_port.pins()[pin_id]); + /* Configure the net sink */ + module_manager.add_module_net_sink(cb_module, net, cb_module, 0, cb_output_port_id, cb_output_port.pins()[pin_id]); + } + + /* Instanciate memory modules */ + /* Find the name and module id of the memory module */ + std::string mem_module_name = generate_mux_subckt_name(circuit_lib, mux_model, datapath_mux_size, std::string(MEMORY_MODULE_POSTFIX)); + ModuleId mem_module = module_manager.find_module(mem_module_name); + VTR_ASSERT (true == module_manager.valid_module_id(mem_module)); + + size_t mem_instance_id = module_manager.num_instance(cb_module, mem_module); + module_manager.add_child_module(cb_module, mem_module); + + /* Give an instance name: this name should be consistent with the block name given in bitstream manager, + * If you want to bind the bitstream generation to modules + */ + std::string mem_instance_name = generate_cb_memory_instance_name(CONNECTION_BLOCK_MEM_INSTANCE_PREFIX, rr_graph.node_side(rr_gsb.get_ipin_node(cb_ipin_side, ipin_index)), ipin_index, std::string("")); + module_manager.set_child_instance_name(cb_module, mem_module, mem_instance_id, mem_instance_name); + + /* Add nets to connect regular and mode-select SRAM ports to the SRAM port of memory module */ + add_module_nets_between_logic_and_memory_sram_bus(module_manager, cb_module, + mux_module, mux_instance_id, + mem_module, mem_instance_id, + circuit_lib, mux_model); + /* Update memory and instance list */ + module_manager.add_configurable_child(cb_module, mem_module, mem_instance_id); +} + +/******************************************************************** + * Print internal connections of a connection block + * For a IPIN node that is driven by only 1 fan-in, + * a short wire will be created + * For a IPIN node that is driven by more than two fan-ins, + * a routing multiplexer will be instanciated + ********************************************************************/ +static +void build_connection_block_interc_modules(ModuleManager& module_manager, + const ModuleId& cb_module, + const VprDeviceAnnotation& device_annotation, + const RRGraph& rr_graph, + const RRGSB& rr_gsb, + const t_rr_type& cb_type, + const CircuitLibrary& circuit_lib, + const e_side& cb_ipin_side, + const size_t& ipin_index, + const std::map& input_port_to_module_nets) { + const RRNodeId& src_rr_node = rr_gsb.get_ipin_node(cb_ipin_side, ipin_index); + + if (1 > rr_graph.node_in_edges(src_rr_node).size()) { + return; /* This port has no driver, skip it */ + } else if (1 == rr_graph.node_in_edges(src_rr_node).size()) { + /* Print a direct connection */ + build_connection_block_module_short_interc(module_manager, cb_module, rr_graph, rr_gsb, cb_type, src_rr_node, input_port_to_module_nets); + + } else if (1 < rr_graph.node_in_edges(src_rr_node).size()) { + /* Print the multiplexer, fan_in >= 2 */ + build_connection_block_mux_module(module_manager, + cb_module, device_annotation, + rr_graph, rr_gsb, cb_type, + circuit_lib, + cb_ipin_side, ipin_index, + input_port_to_module_nets); + } /*Nothing should be done else*/ +} + +/******************************************************************** + * Generate a module of a connection Box (Type: [CHANX|CHANY]) + * Actually it is very similiar to switch box but + * the difference is connection boxes connect Grid INPUT Pins to channels + * NOTE: direct connection between CLBs should NOT be included inside this + * module! They should be added in the top-level module as their connection + * is not limited to adjacent CLBs!!! + * + * Location of a X- and Y-direction Connection Block in FPGA fabric + * +------------+ +-------------+ + * | |------>| | + * | CLB |<------| Y-direction | + * | | ... | Connection | + * | |------>| Block | + * +------------+ +-------------+ + * | ^ ... | | ^ ... | + * v | v v | v + * +-------------------+ +-------------+ + * --->| |--->| | + * <---| X-direction |<---| Switch | + * ...| Connection block |... | Block | + * --->| |--->| | + * +-------------------+ +-------------+ + * + * Internal structure: + * This is an example of a X-direction connection block + * Note that middle output ports are shorted wire from inputs of routing tracks, + * which are also the inputs of routing multiplexer of the connection block + * + * CLB Input Pins + * (IPINs) + * ^ ^ ^ + * | | ... | + * +--------------------------+ + * | ^ ^ ^ | + * | | | ... | | + * | +--------------------+ | + * | | routing | | + * | | multiplexers | | + * | +--------------------+ | + * | middle outputs | + * | of routing channel | + * | ^ ^ ^ ^ ^ ^ ^ ^ | + * | | | | | ... | | | | | + * in[0] -->|------------------------->|---> out[0] + * out[1] <--|<-------------------------|<--- in[1] + * | ... | + * in[W-2] -->|------------------------->|---> out[W-2] + * out[W-1] <--|<-------------------------|<--- in[W-1] + * +--------------------------+ + * + * W: routing channel width + * + ********************************************************************/ +static +void build_connection_block_module(ModuleManager& module_manager, + const VprDeviceAnnotation& device_annotation, + const RRGraph& rr_graph, + const CircuitLibrary& circuit_lib, + const e_config_protocol_type& sram_orgz_type, + const CircuitModelId& sram_model, + const RRGSB& rr_gsb, + const t_rr_type& cb_type, + const bool& verbose) { + /* Create the netlist */ + vtr::Point gsb_coordinate(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); + + /* Create a Verilog Module based on the circuit model, and add to module manager */ + ModuleId cb_module = module_manager.add_module(generate_connection_block_module_name(cb_type, gsb_coordinate)); + + VTR_LOGV(verbose, + "Building module '%s'...", + generate_connection_block_module_name(cb_type, gsb_coordinate).c_str()); + + /* Add the input and output ports of routing tracks in the channel + * Routing tracks pass through the connection blocks + */ + for (size_t itrack = 0; itrack < rr_gsb.get_cb_chan_width(cb_type); ++itrack) { + vtr::Point port_coord(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); + std::string port_name = generate_cb_module_track_port_name(cb_type, + itrack, + IN_PORT); + BasicPort module_port(port_name, 1); /* Every track has a port size of 1 */ + module_manager.add_port(cb_module, module_port, ModuleManager::MODULE_INPUT_PORT); + } + for (size_t itrack = 0; itrack < rr_gsb.get_cb_chan_width(cb_type); ++itrack) { + vtr::Point port_coord(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); + std::string port_name = generate_cb_module_track_port_name(cb_type, + itrack, + OUT_PORT); + BasicPort module_port(port_name, 1); /* Every track has a port size of 1 */ + module_manager.add_port(cb_module, module_port, ModuleManager::MODULE_OUTPUT_PORT); + } + + /* Add the input pins of grids, which are output ports of the connection block */ + std::vector cb_ipin_sides = rr_gsb.get_cb_ipin_sides(cb_type); + for (size_t iside = 0; iside < cb_ipin_sides.size(); ++iside) { + enum e_side cb_ipin_side = cb_ipin_sides[iside]; + for (size_t inode = 0; inode < rr_gsb.get_num_ipin_nodes(cb_ipin_side); ++inode) { + const RRNodeId& ipin_node = rr_gsb.get_ipin_node(cb_ipin_side, inode); + vtr::Point port_coord(rr_graph.node_xlow(ipin_node), rr_graph.node_ylow(ipin_node)); + std::string port_name = generate_cb_module_grid_port_name(cb_ipin_side, + rr_graph.node_pin_num(ipin_node)); + BasicPort module_port(port_name, 1); /* Every grid output has a port size of 1 */ + /* Grid outputs are inputs of switch blocks */ + module_manager.add_port(cb_module, module_port, ModuleManager::MODULE_OUTPUT_PORT); + } + } + + /* Create a cache (fast look up) for module nets whose source are input ports */ + std::map input_port_to_module_nets; + + /* Generate short-wire connection for each routing track : + * Each input port is short-wired to its output port and middle output port + * + * in[i] ----------> out[i] + * | + * +-----> mid_out[i] + */ + for (size_t itrack = 0; itrack < rr_gsb.get_cb_chan_width(cb_type); ++itrack) { + vtr::Point port_coord(rr_gsb.get_cb_x(cb_type), rr_gsb.get_cb_y(cb_type)); + /* Create a port description for the input */ + std::string input_port_name = generate_cb_module_track_port_name(cb_type, + itrack, + IN_PORT); + ModulePortId input_port_id = module_manager.find_module_port(cb_module, input_port_name); + BasicPort input_port = module_manager.module_port(cb_module, input_port_id); + + /* Create a port description for the output */ + std::string output_port_name = generate_cb_module_track_port_name(cb_type, + itrack, + OUT_PORT); + ModulePortId output_port_id = module_manager.find_module_port(cb_module, output_port_name); + BasicPort output_port = module_manager.module_port(cb_module, output_port_id); + + /* Ensure port size matching */ + VTR_ASSERT(1 == input_port.get_width()); + VTR_ASSERT(input_port.get_width() == output_port.get_width()); + + /* Create short-wires: input port ---> output port + * Do short-wires: input port ---> middle output port + */ + for (size_t pin_id = 0; pin_id < input_port.pins().size(); ++pin_id) { + ModuleNetId net = module_manager.create_module_net(cb_module); + module_manager.add_module_net_source(cb_module, net, cb_module, 0, input_port_id, input_port.pins()[pin_id]); + module_manager.add_module_net_sink(cb_module, net, cb_module, 0, output_port_id, output_port.pins()[pin_id]); + /* Cache the module net */ + input_port_to_module_nets[input_port_id] = net; + } + } + + /* Add sub modules of routing multiplexers or direct interconnect*/ + for (size_t iside = 0; iside < cb_ipin_sides.size(); ++iside) { + enum e_side cb_ipin_side = cb_ipin_sides[iside]; + for (size_t inode = 0; inode < rr_gsb.get_num_ipin_nodes(cb_ipin_side); ++inode) { + build_connection_block_interc_modules(module_manager, + cb_module, device_annotation, + rr_graph, + rr_gsb, cb_type, + circuit_lib, + cb_ipin_side, inode, + input_port_to_module_nets); + } + } + + /* Add global ports to the pb_module: + * This is a much easier job after adding sub modules (instances), + * we just need to find all the global ports from the child modules and build a list of it + */ + add_module_global_ports_from_child_modules(module_manager, cb_module); + + /* Count shared SRAM ports from the sub-modules under this Verilog module + * This is a much easier job after adding sub modules (instances), + * we just need to find all the I/O ports from the child modules and build a list of it + */ + size_t module_num_shared_config_bits = find_module_num_shared_config_bits_from_child_modules(module_manager, cb_module); + if (0 < module_num_shared_config_bits) { + add_reserved_sram_ports_to_module_manager(module_manager, cb_module, module_num_shared_config_bits); + } + + /* Count SRAM ports from the sub-modules under this Verilog module + * This is a much easier job after adding sub modules (instances), + * we just need to find all the I/O ports from the child modules and build a list of it + */ + size_t module_num_config_bits = find_module_num_config_bits_from_child_modules(module_manager, cb_module, circuit_lib, sram_model, sram_orgz_type); + if (0 < module_num_config_bits) { + add_sram_ports_to_module_manager(module_manager, cb_module, circuit_lib, sram_model, sram_orgz_type, module_num_config_bits); + } + + /* Add all the nets to connect configuration ports from memory module to primitive modules + * This is a one-shot addition that covers all the memory modules in this primitive module! + */ + if (0 < module_manager.configurable_children(cb_module).size()) { + add_module_nets_memory_config_bus(module_manager, cb_module, + sram_orgz_type, circuit_lib.design_tech_type(sram_model)); + } + + VTR_LOGV(verbose, "Done\n"); +} + + +/******************************************************************** + * Iterate over all the connection blocks in a device + * and build a module for each of them + *******************************************************************/ +static +void build_flatten_connection_block_modules(ModuleManager& module_manager, + const DeviceContext& device_ctx, + const VprDeviceAnnotation& device_annotation, + const DeviceRRGSB& device_rr_gsb, + const CircuitLibrary& circuit_lib, + const e_config_protocol_type& sram_orgz_type, + const CircuitModelId& sram_model, + const t_rr_type& cb_type, + const bool& verbose) { + /* Build unique X-direction connection block modules */ + vtr::Point cb_range = device_rr_gsb.get_gsb_range(); + + for (size_t ix = 0; ix < cb_range.x(); ++ix) { + for (size_t iy = 0; iy < cb_range.y(); ++iy) { + /* Check if the connection block exists in the device! + * Some of them do NOT exist due to heterogeneous blocks (height > 1) + * We will skip those modules + */ + const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); + if (false == rr_gsb.is_cb_exist(cb_type)) { + continue; + } + build_connection_block_module(module_manager, + device_annotation, + device_ctx.rr_graph, + circuit_lib, + sram_orgz_type, sram_model, + rr_gsb, cb_type, + verbose); + } + } +} + +/******************************************************************** + * A top-level function of this file + * Build all the modules for global routing architecture of a FPGA fabric + * in a flatten way: + * Each connection block and switch block will be generated as a unique module + * Covering: + * 1. Connection blocks + * 2. Switch blocks + *******************************************************************/ +void build_flatten_routing_modules(ModuleManager& module_manager, + const DeviceContext& device_ctx, + const VprDeviceAnnotation& device_annotation, + const DeviceRRGSB& device_rr_gsb, + const CircuitLibrary& circuit_lib, + const e_config_protocol_type& sram_orgz_type, + const CircuitModelId& sram_model, + const bool& verbose) { + + vtr::ScopedStartFinishTimer timer("Build routing modules..."); + + vtr::Point sb_range = device_rr_gsb.get_gsb_range(); + + /* Build unique switch block modules */ + for (size_t ix = 0; ix < sb_range.x(); ++ix) { + for (size_t iy = 0; iy < sb_range.y(); ++iy) { + const RRGSB& rr_gsb = device_rr_gsb.get_gsb(ix, iy); + if (false == rr_gsb.is_sb_exist()) { + continue; + } + build_switch_block_module(module_manager, + device_annotation, + device_ctx.rr_graph, + circuit_lib, + sram_orgz_type, sram_model, + rr_gsb, + verbose); + } + } + + build_flatten_connection_block_modules(module_manager, + device_ctx, + device_annotation, + device_rr_gsb, + circuit_lib, + sram_orgz_type, sram_model, + CHANX, + verbose); + + build_flatten_connection_block_modules(module_manager, + device_ctx, + device_annotation, + device_rr_gsb, + circuit_lib, + sram_orgz_type, sram_model, + CHANY, + verbose); +} + +/******************************************************************** + * A top-level function of this file + * Build all the unique modules for global routing architecture of a FPGA fabric + * This function will use unique module list built in device_rr_gsb, + * to build only unique modules (in terms of graph connections) of + * 1. Connection blocks + * 2. Switch blocks + * + * Note: this function SHOULD be called only when + * the option compact_routing_hierarchy is turned on!!! + *******************************************************************/ +void build_unique_routing_modules(ModuleManager& module_manager, + const DeviceContext& device_ctx, + const VprDeviceAnnotation& device_annotation, + const DeviceRRGSB& device_rr_gsb, + const CircuitLibrary& circuit_lib, + const e_config_protocol_type& sram_orgz_type, + const CircuitModelId& sram_model, + const bool& verbose) { + + vtr::ScopedStartFinishTimer timer("Build unique routing modules..."); + + /* Build unique switch block modules */ + for (size_t isb = 0; isb < device_rr_gsb.get_num_sb_unique_module(); ++isb) { + const RRGSB& unique_mirror = device_rr_gsb.get_sb_unique_module(isb); + build_switch_block_module(module_manager, + device_annotation, + device_ctx.rr_graph, + circuit_lib, + sram_orgz_type, sram_model, + unique_mirror, + verbose); + } + + /* Build unique X-direction connection block modules */ + for (size_t icb = 0; icb < device_rr_gsb.get_num_cb_unique_module(CHANX); ++icb) { + const RRGSB& unique_mirror = device_rr_gsb.get_cb_unique_module(CHANX, icb); + + build_connection_block_module(module_manager, + device_annotation, + device_ctx.rr_graph, + circuit_lib, + sram_orgz_type, sram_model, + unique_mirror, CHANX, + verbose); + } + + /* Build unique X-direction connection block modules */ + for (size_t icb = 0; icb < device_rr_gsb.get_num_cb_unique_module(CHANY); ++icb) { + const RRGSB& unique_mirror = device_rr_gsb.get_cb_unique_module(CHANY, icb); + + build_connection_block_module(module_manager, + device_annotation, + device_ctx.rr_graph, + circuit_lib, + sram_orgz_type, sram_model, + unique_mirror, CHANY, + verbose); + } +} + +} /* end namespace openfpga */ diff --git a/openfpga/src/fabric/build_routing_modules.h b/openfpga/src/fabric/build_routing_modules.h new file mode 100644 index 000000000..d8eb09784 --- /dev/null +++ b/openfpga/src/fabric/build_routing_modules.h @@ -0,0 +1,41 @@ +#ifndef BUILD_ROUTING_MODULES_H +#define BUILD_ROUTING_MODULES_H + +/******************************************************************** + * Include header files that are required by function declaration + *******************************************************************/ +#include "vpr_context.h" +#include "vpr_device_annotation.h" +#include "device_rr_gsb.h" +#include "mux_library.h" +#include "circuit_library.h" +#include "module_manager.h" + +/******************************************************************** + * Function declaration + *******************************************************************/ + +/* begin namespace openfpga */ +namespace openfpga { + +void build_flatten_routing_modules(ModuleManager& module_manager, + const DeviceContext& device_ctx, + const VprDeviceAnnotation& device_annotation, + const DeviceRRGSB& device_rr_gsb, + const CircuitLibrary& circuit_lib, + const e_config_protocol_type& sram_orgz_type, + const CircuitModelId& sram_model, + const bool& verbose); + +void build_unique_routing_modules(ModuleManager& module_manager, + const DeviceContext& device_ctx, + const VprDeviceAnnotation& device_annotation, + const DeviceRRGSB& device_rr_gsb, + const CircuitLibrary& circuit_lib, + const e_config_protocol_type& sram_orgz_type, + const CircuitModelId& sram_model, + const bool& verbose); + +} /* end namespace openfpga */ + +#endif diff --git a/openfpga/src/utils/openfpga_rr_graph_utils.cpp b/openfpga/src/utils/openfpga_rr_graph_utils.cpp index 21ec1b640..ba789b4da 100644 --- a/openfpga/src/utils/openfpga_rr_graph_utils.cpp +++ b/openfpga/src/utils/openfpga_rr_graph_utils.cpp @@ -81,4 +81,51 @@ std::vector get_rr_graph_driver_switches(const RRGraph& rr_graph, return driver_switches; } +/************************************************************************ + * Find the driver nodes for a node in the rr_graph + ***********************************************************************/ +std::vector get_rr_graph_driver_nodes(const RRGraph& rr_graph, + const RRNodeId& node) { + std::vector driver_nodes; + for (const RREdgeId& edge : rr_graph.node_in_edges(node)) { + driver_nodes.push_back(rr_graph.edge_src_node(edge)); + } + + return driver_nodes; +} + +/************************************************************************ + * Find the configurable driver nodes for a node in the rr_graph + ***********************************************************************/ +std::vector get_rr_graph_configurable_driver_nodes(const RRGraph& rr_graph, + const RRNodeId& node) { + std::vector driver_nodes; + for (const RREdgeId& edge : rr_graph.node_in_edges(node)) { + /* Bypass non-configurable edges */ + if (false == rr_graph.edge_is_configurable(edge)) { + continue; + } + driver_nodes.push_back(rr_graph.edge_src_node(edge)); + } + + return driver_nodes; +} + +/************************************************************************ + * Find the configurable driver nodes for a node in the rr_graph + ***********************************************************************/ +std::vector get_rr_graph_non_configurable_driver_nodes(const RRGraph& rr_graph, + const RRNodeId& node) { + std::vector driver_nodes; + for (const RREdgeId& edge : rr_graph.node_in_edges(node)) { + /* Bypass configurable edges */ + if (true == rr_graph.edge_is_configurable(edge)) { + continue; + } + driver_nodes.push_back(rr_graph.edge_src_node(edge)); + } + + return driver_nodes; +} + } /* end namespace openfpga */ diff --git a/openfpga/src/utils/openfpga_rr_graph_utils.h b/openfpga/src/utils/openfpga_rr_graph_utils.h index 0c4d174c6..0466892f8 100644 --- a/openfpga/src/utils/openfpga_rr_graph_utils.h +++ b/openfpga/src/utils/openfpga_rr_graph_utils.h @@ -26,6 +26,15 @@ vtr::Point get_track_rr_node_end_coordinate(const RRGraph& rr_graph, std::vector get_rr_graph_driver_switches(const RRGraph& rr_graph, const RRNodeId& node); +std::vector get_rr_graph_driver_nodes(const RRGraph& rr_graph, + const RRNodeId& node); + +std::vector get_rr_graph_configurable_driver_nodes(const RRGraph& rr_graph, + const RRNodeId& node); + +std::vector get_rr_graph_non_configurable_driver_nodes(const RRGraph& rr_graph, + const RRNodeId& node); + } /* end namespace openfpga */ #endif